diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 0bbee38acd263f6e88605ac71e3bd12c8ca3bf70..72aff61e731509d7c87f3a3913aa3b4e965ab9c5 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -753,8 +753,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     [Multiple options for each card instance]
     model	- force the model name
-    position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
+    position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
+    bdl_pos_adj	- Specifies the DMA IRQ timing delay in samples.
+		Passing -1 will make the driver to choose the appropriate
+		value based on the controller chip.
     
     [Single (global) options]
     single_cmd  - Use single immediate commands to communicate with
@@ -845,7 +848,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	ALC269
 	  basic		Basic preset
 
-	ALC662
+	ALC662/663
 	  3stack-dig	3-stack (2-channel) with SPDIF
 	  3stack-6ch	 3-stack (6-channel)
 	  3stack-6ch-dig 3-stack (6-channel) with SPDIF
@@ -853,6 +856,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  lenovo-101e	 Lenovo laptop
 	  eeepc-p701	ASUS Eeepc P701
 	  eeepc-ep20	ASUS Eeepc EP20
+	  m51va		ASUS M51VA
+	  g71v		ASUS G71V
+	  h13		ASUS H13
+	  g50v		ASUS G50V
 	  auto		auto-config reading BIOS (default)
 
 	ALC882/885
@@ -1091,7 +1098,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     This occurs when the access to non-existing or non-working codec slot
     (likely a modem one) causes a stall of the communication via HD-audio
     bus.  You can see which codec slots are probed by enabling
-    CONFIG_SND_DEBUG_DETECT, or simply from the file name of the codec
+    CONFIG_SND_DEBUG_VERBOSE, or simply from the file name of the codec
     proc files.  Then limit the slots to probe by probe_mask option.
     For example, probe_mask=1 means to probe only the first slot, and
     probe_mask=4 means only the third slot.
@@ -2267,6 +2274,10 @@ case above again, the first two slots are already reserved.  If any
 other driver (e.g. snd-usb-audio) is loaded before snd-interwave or
 snd-ens1371, it will be assigned to the third or later slot.
 
+When a module name is given with '!', the slot will be given for any
+modules but that name.  For example, "slots=!snd-pcsp" will reserve
+the first slot for any modules but snd-pcsp. 
+
 
 ALSA PCM devices to OSS devices mapping
 =======================================
diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
index b03df4d4795c6767b79bed4125b07bbdf64fc823..e13c4e67029f497899d4cbc545f67fd073d36e1f 100644
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
@@ -6127,8 +6127,8 @@ struct _snd_pcm_runtime {
 
       <para>
         <function>snd_printdd()</function> is compiled in only when
-      <constant>CONFIG_SND_DEBUG_DETECT</constant> is set. Please note
-      that <constant>DEBUG_DETECT</constant> is not set as default
+      <constant>CONFIG_SND_DEBUG_VERBOSE</constant> is set. Please note
+      that <constant>CONFIG_SND_DEBUG_VERBOSE</constant> is not set as default
       even if you configure the alsa-driver with
       <option>--with-debug=full</option> option. You need to give
       explicitly <option>--with-debug=detect</option> option instead. 
diff --git a/include/asm-mips/mach-au1x00/au1xxx_psc.h b/include/asm-mips/mach-au1x00/au1xxx_psc.h
index dae4eca2417e847fd47a3eb8f2009814f8c07a77..892b7f168eb47df60d0b18cf2d0abb26848ac552 100644
--- a/include/asm-mips/mach-au1x00/au1xxx_psc.h
+++ b/include/asm-mips/mach-au1x00/au1xxx_psc.h
@@ -204,6 +204,14 @@ typedef struct	psc_i2s {
 	u32	psc_i2sudf;
 } psc_i2s_t;
 
+#define PSC_I2SCFG_OFFSET	0x08
+#define PSC_I2SMASK_OFFSET	0x0C
+#define PSC_I2SPCR_OFFSET	0x10
+#define PSC_I2SSTAT_OFFSET	0x14
+#define PSC_I2SEVENT_OFFSET	0x18
+#define PSC_I2SRXTX_OFFSET	0x1C
+#define PSC_I2SUDF_OFFSET	0x20
+
 /* I2S Config Register. */
 #define PSC_I2SCFG_RT_MASK	(3 << 30)
 #define PSC_I2SCFG_RT_FIFO1	(0 << 30)
diff --git a/include/sound/ad1843.h b/include/sound/ad1843.h
new file mode 100644
index 0000000000000000000000000000000000000000..b236a9d1d6e4f4f54c136f2e5f633cb023d1bdc2
--- /dev/null
+++ b/include/sound/ad1843.h
@@ -0,0 +1,46 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ * Copyright 2008 Thomas Bogendoerfer <tsbogend@franken.de>
+ */
+
+#ifndef __SOUND_AD1843_H
+#define __SOUND_AD1843_H
+
+struct snd_ad1843 {
+	void *chip;
+	int (*read)(void *chip, int reg);
+	int (*write)(void *chip, int reg, int val);
+};
+
+#define AD1843_GAIN_RECLEV 0
+#define AD1843_GAIN_LINE   1
+#define AD1843_GAIN_LINE_2 2
+#define AD1843_GAIN_MIC    3
+#define AD1843_GAIN_PCM_0  4
+#define AD1843_GAIN_PCM_1  5
+#define AD1843_GAIN_SIZE   (AD1843_GAIN_PCM_1+1)
+
+int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id);
+int ad1843_get_gain(struct snd_ad1843 *ad1843, int id);
+int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval);
+int ad1843_get_recsrc(struct snd_ad1843 *ad1843);
+int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc);
+void ad1843_setup_dac(struct snd_ad1843 *ad1843,
+		      unsigned int id,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels);
+void ad1843_shutdown_dac(struct snd_ad1843 *ad1843,
+			 unsigned int id);
+void ad1843_setup_adc(struct snd_ad1843 *ad1843,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels);
+void ad1843_shutdown_adc(struct snd_ad1843 *ad1843);
+int ad1843_init(struct snd_ad1843 *ad1843);
+
+#endif /* __SOUND_AD1843_H */
diff --git a/include/sound/control.h b/include/sound/control.h
index 3dc1291f52db78e8f525801f1b79204180e91dbf..4721b4bba0538360cd808db6c6cc0f13db9a3c7a 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -129,9 +129,6 @@ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn);
 #define snd_ctl_unregister_ioctl_compat(fcn)
 #endif
 
-int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control);
-int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_ctl_elem_value *control);
-
 static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
 {
 	return id->numid - kctl->id.numid;
diff --git a/include/sound/core.h b/include/sound/core.h
index 695ee53488a382ab85d90f1deb5e3b83a5fe82e6..558b96284bd22eba399ff2e8f28147a5c46b2eb0 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -412,13 +412,13 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
 
 #endif /* CONFIG_SND_DEBUG */
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 /**
  * snd_printdd - debug printk
  * @format: format string
  *
  * Works like snd_printk() for debugging purposes.
- * Ignored when CONFIG_SND_DEBUG_DETECT is not set.
+ * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
  */
 #define snd_printdd(format, args...) snd_printk(format, ##args)
 #else
@@ -442,7 +442,7 @@ struct snd_pci_quirk {
 	unsigned short subvendor;	/* PCI subvendor ID */
 	unsigned short subdevice;	/* PCI subdevice ID */
 	int value;			/* value */
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	const char *name;		/* name of the device (optional) */
 #endif
 };
@@ -450,7 +450,7 @@ struct snd_pci_quirk {
 #define _SND_PCI_QUIRK_ID(vend,dev) \
 	.subvendor = (vend), .subdevice = (dev)
 #define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 #define SND_PCI_QUIRK(vend,dev,xname,val) \
 	{_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)}
 #else
diff --git a/include/sound/cs4231-regs.h b/include/sound/cs4231-regs.h
index e8d1f3e31f9e5c1eebc42ab6be4cdb9a1d8b3ebf..92647532c454aff379a627492211fcfac96d7abc 100644
--- a/include/sound/cs4231-regs.h
+++ b/include/sound/cs4231-regs.h
@@ -177,4 +177,12 @@
 #define CS4236_RIGHT_WAVE	0x1c	/* right wavetable serial port volume */
 #define CS4236_VERSION		0x9c	/* chip version and ID */
 
+/* definitions for extended registers - OPTI93X */
+#define OPTi931_AUX_LEFT_INPUT	0x10
+#define OPTi931_AUX_RIGHT_INPUT	0x11
+#define OPTi93X_MIC_LEFT_INPUT	0x14
+#define OPTi93X_MIC_RIGHT_INPUT	0x15
+#define OPTi93X_OUT_LEFT	0x16
+#define OPTi93X_OUT_RIGHT	0x17
+
 #endif /* __SOUND_CS4231_REGS_H */
diff --git a/include/sound/cs4231.h b/include/sound/cs4231.h
index 66055d702aa3cadf6c77544a31df05ff248960f9..f0785f9f4ae4939fc6289f53d1572e9f7802f4b1 100644
--- a/include/sound/cs4231.h
+++ b/include/sound/cs4231.h
@@ -58,6 +58,7 @@
 /* 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)
@@ -120,6 +121,8 @@ 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);
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 7b7b9b13b4ddd8d04c1cf6392d15c95d58972de0..10ee28eac018535da6f1e706b9c6189f90d46230 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -1670,6 +1670,7 @@ struct snd_emu_chip_details {
 	unsigned char spi_dac;      /* SPI interface for DAC */
 	unsigned char i2c_adc;      /* I2C interface for ADC */
 	unsigned char adc_1361t;    /* Use Philips 1361T ADC */
+	unsigned char invert_shared_spdif; /* analog/digital switch inverted */
 	const char *driver;
 	const char *name;
 	const char *id;		/* for backward compatibility - can be NULL if not needed */
diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h
index f023c1b97f8caa93cee30e8ed91f27a270c3b7a1..3d9afb6a8c9cd86b5ead0d9ccccaa49614f47fd8 100644
--- a/include/sound/seq_kernel.h
+++ b/include/sound/seq_kernel.h
@@ -105,7 +105,7 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp,
 			      int cap, int type, int midi_channels, int midi_voices, char *portname);
 int snd_seq_event_port_detach(int client, int port);
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 void snd_seq_autoload_lock(void);
 void snd_seq_autoload_unlock(void);
 #else
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index a105b01e06d50dc3ccb8b8bcb527d64ea9b7cbe8..3030fdc6981d05b804ccb5e31d5d7d3fafd7565e 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -130,6 +130,13 @@
 {	.id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
 	.shift = wshift, .invert = winvert}
 
+/* generic register modifier widget */
+#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
+{	.id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \
+	.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
+	.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
+	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
+
 /* dapm kcontrol types */
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -193,6 +200,7 @@ struct snd_soc_dapm_widget;
 enum snd_soc_dapm_type;
 struct snd_soc_dapm_path;
 struct snd_soc_dapm_pin;
+struct snd_soc_dapm_route;
 
 /* dapm controls */
 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
@@ -205,25 +213,32 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
 	const struct snd_soc_dapm_widget *widget);
+int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+	const struct snd_soc_dapm_widget *widget,
+	int num);
 
 /* dapm path setup */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
+int  __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
 	const char *sink_name, const char *control_name, const char *src_name);
 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
 void snd_soc_dapm_free(struct snd_soc_device *socdev);
+int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+			    const struct snd_soc_dapm_route *route, int num);
 
 /* dapm events */
 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
 	int event);
-int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event);
+int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+	enum snd_soc_bias_level level);
 
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
 
-/* dapm audio endpoint control */
-int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
-	char *pin, int status);
-int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec);
+/* dapm audio pin control and status */
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_sync(struct snd_soc_codec *codec);
 
 /* dapm widget types */
 enum snd_soc_dapm_type {
@@ -245,6 +260,18 @@ enum snd_soc_dapm_type {
 	snd_soc_dapm_post,			/* machine specific post widget - exec last */
 };
 
+/*
+ * DAPM audio route definition.
+ *
+ * Defines an audio route originating at source via control and finishing
+ * at sink.
+ */
+struct snd_soc_dapm_route {
+	const char *sink;
+	const char *control;
+	const char *source;
+};
+
 /* dapm audio path between two widgets */
 struct snd_soc_dapm_path {
 	char *name;
@@ -277,6 +304,9 @@ struct snd_soc_dapm_widget {
 	unsigned char shift;			/* bits to shift */
 	unsigned int saved_value;		/* widget saved value */
 	unsigned int value;				/* widget current value */
+	unsigned int mask;			/* non-shifted mask */
+	unsigned int on_val;			/* on state value */
+	unsigned int off_val;			/* off state value */
 	unsigned char power:1;			/* block power status */
 	unsigned char invert:1;			/* invert the power bit */
 	unsigned char active:1;			/* active stream on DAC, ADC's */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index d3c8c033dff83b06d772e4dab9178f35413e17ff..1890d87c52042adc4d375953195a350d20e198ea 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -73,6 +73,15 @@
 	.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) \
+{	.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) \
 {	.reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
 	.mask = xmask, .texts = xtexts }
@@ -91,6 +100,15 @@
 	.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,\
+	 xhandler_get, xhandler_put, 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 = xhandler_get, .put = xhandler_put, \
+	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, 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, \
@@ -102,6 +120,24 @@
 	.get = xhandler_get, .put = xhandler_put, \
 	.private_value = (unsigned long)&xenum }
 
+/*
+ * Bias levels
+ *
+ * @ON:      Bias is fully on for audio playback and capture operations.
+ * @PREPARE: Prepare for audio operations. Called before DAPM switching for
+ *           stream start and stop operations.
+ * @STANDBY: Low power standby state when no playback/capture operations are
+ *           in progress. NOTE: The transition time between STANDBY and ON
+ *           should be as fast as possible and no longer than 10ms.
+ * @OFF:     Power Off. No restrictions on transition times.
+ */
+enum snd_soc_bias_level {
+	SND_SOC_BIAS_ON,
+	SND_SOC_BIAS_PREPARE,
+	SND_SOC_BIAS_STANDBY,
+	SND_SOC_BIAS_OFF,
+};
+
 /*
  * Digital Audio Interface (DAI) types
  */
@@ -185,8 +221,7 @@ struct snd_soc_pcm_stream;
 struct snd_soc_ops;
 struct snd_soc_dai_mode;
 struct snd_soc_pcm_runtime;
-struct snd_soc_codec_dai;
-struct snd_soc_cpu_dai;
+struct snd_soc_dai;
 struct snd_soc_codec;
 struct snd_soc_machine_config;
 struct soc_enum;
@@ -221,6 +256,27 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 	struct snd_ac97_bus_ops *ops, int num);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
+/* Digital Audio Interface clocking API.*/
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+	unsigned int freq, int dir);
+
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+	int div_id, int div);
+
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+	int pll_id, unsigned int freq_in, unsigned int freq_out);
+
+/* Digital Audio interface formatting */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
+
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int mask, int slots);
+
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
+
+/* Digital Audio Interface mute */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
+
 /*
  *Controls
  */
@@ -249,6 +305,12 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 
 /* SoC PCM stream information */
 struct snd_soc_pcm_stream {
@@ -272,87 +334,45 @@ struct snd_soc_ops {
 	int (*trigger)(struct snd_pcm_substream *, int);
 };
 
-/* ASoC codec DAI ops */
-struct snd_soc_codec_ops {
-	/* codec DAI clocking configuration */
-	int (*set_sysclk)(struct snd_soc_codec_dai *codec_dai,
+/* ASoC DAI ops */
+struct snd_soc_dai_ops {
+	/* DAI clocking configuration */
+	int (*set_sysclk)(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir);
-	int (*set_pll)(struct snd_soc_codec_dai *codec_dai,
+	int (*set_pll)(struct snd_soc_dai *dai,
 		int pll_id, unsigned int freq_in, unsigned int freq_out);
-	int (*set_clkdiv)(struct snd_soc_codec_dai *codec_dai,
-		int div_id, int div);
+	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 
-	/* CPU DAI format configuration */
-	int (*set_fmt)(struct snd_soc_codec_dai *codec_dai,
-		unsigned int fmt);
-	int (*set_tdm_slot)(struct snd_soc_codec_dai *codec_dai,
+	/* DAI format configuration */
+	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
+	int (*set_tdm_slot)(struct snd_soc_dai *dai,
 		unsigned int mask, int slots);
-	int (*set_tristate)(struct snd_soc_codec_dai *, int tristate);
+	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
 	/* digital mute */
-	int (*digital_mute)(struct snd_soc_codec_dai *, int mute);
-};
-
-/* ASoC cpu DAI ops */
-struct snd_soc_cpu_ops {
-	/* CPU DAI clocking configuration */
-	int (*set_sysclk)(struct snd_soc_cpu_dai *cpu_dai,
-		int clk_id, unsigned int freq, int dir);
-	int (*set_clkdiv)(struct snd_soc_cpu_dai *cpu_dai,
-		int div_id, int div);
-	int (*set_pll)(struct snd_soc_cpu_dai *cpu_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out);
-
-	/* CPU DAI format configuration */
-	int (*set_fmt)(struct snd_soc_cpu_dai *cpu_dai,
-		unsigned int fmt);
-	int (*set_tdm_slot)(struct snd_soc_cpu_dai *cpu_dai,
-		unsigned int mask, int slots);
-	int (*set_tristate)(struct snd_soc_cpu_dai *, int tristate);
-};
-
-/* SoC Codec DAI */
-struct snd_soc_codec_dai {
-	char *name;
-	int id;
-	unsigned char type;
-
-	/* DAI capabilities */
-	struct snd_soc_pcm_stream playback;
-	struct snd_soc_pcm_stream capture;
-
-	/* DAI runtime info */
-	struct snd_soc_codec *codec;
-	unsigned int active;
-	unsigned char pop_wait:1;
-
-	/* ops */
-	struct snd_soc_ops ops;
-	struct snd_soc_codec_ops dai_ops;
-
-	/* DAI private data */
-	void *private_data;
+	int (*digital_mute)(struct snd_soc_dai *dai, int mute);
 };
 
-/* SoC CPU DAI */
-struct snd_soc_cpu_dai {
-
+/* SoC  DAI (Digital Audio Interface) */
+struct snd_soc_dai {
 	/* DAI description */
 	char *name;
 	unsigned int id;
 	unsigned char type;
 
 	/* DAI callbacks */
-	int (*probe)(struct platform_device *pdev);
-	void (*remove)(struct platform_device *pdev);
+	int (*probe)(struct platform_device *pdev,
+		     struct snd_soc_dai *dai);
+	void (*remove)(struct platform_device *pdev,
+		       struct snd_soc_dai *dai);
 	int (*suspend)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 	int (*resume)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 
 	/* ops */
 	struct snd_soc_ops ops;
-	struct snd_soc_cpu_ops dai_ops;
+	struct snd_soc_dai_ops dai_ops;
 
 	/* DAI capabilities */
 	struct snd_soc_pcm_stream capture;
@@ -360,7 +380,9 @@ struct snd_soc_cpu_dai {
 
 	/* DAI runtime info */
 	struct snd_pcm_runtime *runtime;
-	unsigned char active:1;
+	struct snd_soc_codec *codec;
+	unsigned int active;
+	unsigned char pop_wait:1;
 	void *dma_data;
 
 	/* DAI private data */
@@ -374,7 +396,8 @@ struct snd_soc_codec {
 	struct mutex mutex;
 
 	/* callbacks */
-	int (*dapm_event)(struct snd_soc_codec *codec, int event);
+	int (*set_bias_level)(struct snd_soc_codec *,
+			      enum snd_soc_bias_level level);
 
 	/* runtime */
 	struct snd_card *card;
@@ -396,12 +419,12 @@ struct snd_soc_codec {
 	/* dapm */
 	struct list_head dapm_widgets;
 	struct list_head dapm_paths;
-	unsigned int dapm_state;
-	unsigned int suspend_dapm_state;
+	enum snd_soc_bias_level bias_level;
+	enum snd_soc_bias_level suspend_bias_level;
 	struct delayed_work delayed_work;
 
 	/* codec DAI's */
-	struct snd_soc_codec_dai *dai;
+	struct snd_soc_dai *dai;
 	unsigned int num_dai;
 };
 
@@ -420,12 +443,12 @@ struct snd_soc_platform {
 	int (*probe)(struct platform_device *pdev);
 	int (*remove)(struct platform_device *pdev);
 	int (*suspend)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 	int (*resume)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 
 	/* pcm creation and destruction */
-	int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *,
+	int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
 		struct snd_pcm *);
 	void (*pcm_free)(struct snd_pcm *);
 
@@ -439,8 +462,8 @@ struct snd_soc_dai_link  {
 	char *stream_name;		/* Stream name */
 
 	/* DAI */
-	struct snd_soc_codec_dai *codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai;
+	struct snd_soc_dai *codec_dai;
+	struct snd_soc_dai *cpu_dai;
 
 	/* machine stream operations */
 	struct snd_soc_ops *ops;
@@ -467,7 +490,8 @@ struct snd_soc_machine {
 	int (*resume_post)(struct platform_device *pdev);
 
 	/* callbacks */
-	int (*dapm_event)(struct snd_soc_machine *, int event);
+	int (*set_bias_level)(struct snd_soc_machine *,
+			      enum snd_soc_bias_level level);
 
 	/* CPU <--> Codec DAI links  */
 	struct snd_soc_dai_link *dai_link;
@@ -482,6 +506,7 @@ struct snd_soc_device {
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec_device *codec_dev;
 	struct delayed_work delayed_work;
+	struct work_struct deferred_resume_work;
 	void *codec_data;
 };
 
diff --git a/include/sound/uda1341.h b/include/sound/uda1341.h
index 2e564bfb37fea135053d7993991b0d53e31597bd..110d5dc3a2bed878815abc5abf3c03a17a31e43e 100644
--- a/include/sound/uda1341.h
+++ b/include/sound/uda1341.h
@@ -15,8 +15,6 @@
  *                           features support
  */
 
-/* $Id: uda1341.h,v 1.8 2005/11/17 14:17:21 tiwai Exp $ */
-
 #define UDA1341_ALSA_NAME "snd-uda1341"
 
 /*
diff --git a/include/sound/version.h b/include/sound/version.h
index ed6fb2eb1eac210d8812bfe26016ff9dd2bbeab0..6b78aff273a8a812fd303baa7b2f7f2bae1ca969 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
-/* include/version.h.  Generated by alsa/ksync script.  */
-#define CONFIG_SND_VERSION "1.0.16"
+/* include/version.h */
+#define CONFIG_SND_VERSION "1.0.17"
 #define CONFIG_SND_DATE ""
diff --git a/sound/Kconfig b/sound/Kconfig
index 4247406160e7ef7c5684943a9c3065e1ec73eb9a..a37bee094eba4482c612612ae42366002e3b1134 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -1,11 +1,9 @@
 # sound/Config.in
 #
 
-menu "Sound"
-	depends on HAS_IOMEM
-
-config SOUND
+menuconfig SOUND
 	tristate "Sound card support"
+	depends on HAS_IOMEM
 	help
 	  If you have a sound card in your computer, i.e. if it can say more
 	  than an occasional beep, say Y.  Be sure to have all the information
@@ -28,22 +26,22 @@ config SOUND
 	  and read <file:Documentation/sound/oss/README.modules>; the module
 	  will be called soundcore.
 
+if SOUND
+
 source "sound/oss/dmasound/Kconfig"
 
 if !M68K
 
-menu "Advanced Linux Sound Architecture"
-	depends on SOUND!=n
-
-config SND
+menuconfig SND
 	tristate "Advanced Linux Sound Architecture"
-	depends on SOUND
 	help
 	  Say 'Y' or 'M' to enable ALSA (Advanced Linux Sound Architecture),
 	  the new base sound system.
 
 	  For more information, see <http://www.alsa-project.org/>
 
+if SND
+
 source "sound/core/Kconfig"
 
 source "sound/drivers/Kconfig"
@@ -58,9 +56,7 @@ source "sound/aoa/Kconfig"
 
 source "sound/arm/Kconfig"
 
-if SPI
 source "sound/spi/Kconfig"
-endif
 
 source "sound/mips/Kconfig"
 
@@ -80,22 +76,20 @@ source "sound/parisc/Kconfig"
 
 source "sound/soc/Kconfig"
 
-endmenu
+endif # SND
 
-menu "Open Sound System"
-	depends on SOUND!=n
-
-config SOUND_PRIME
+menuconfig SOUND_PRIME
 	tristate "Open Sound System (DEPRECATED)"
-	depends on SOUND
 	help
 	  Say 'Y' or 'M' to enable Open Sound System drivers.
 
+if SOUND_PRIME
+
 source "sound/oss/Kconfig"
 
-endmenu
+endif # SOUND_PRIME
 
-endif
+endif # !M68K
 
 config AC97_BUS
 	tristate
@@ -105,4 +99,4 @@ config AC97_BUS
 	  sound although they're sharing the AC97 bus. Concerned drivers
 	  should "select" this.
 
-endmenu
+endif # SOUND
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index 5d5813cec4c859a1dafd4dc2d38d45029adf91c9..c081e18b95400fac658a4821c87373d200906c05 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -1,18 +1,17 @@
-menu "Apple Onboard Audio driver"
-	depends on SND!=n && PPC_PMAC
-
-config SND_AOA
+menuconfig SND_AOA
 	tristate "Apple Onboard Audio driver"
-	depends on SND
+	depends on PPC_PMAC
 	select SND_PCM
 	---help---
 	This option enables the new driver for the various
 	Apple Onboard Audio components.
 
+if SND_AOA
+
 source "sound/aoa/fabrics/Kconfig"
 
 source "sound/aoa/codecs/Kconfig"
 
 source "sound/aoa/soundbus/Kconfig"
 
-endmenu
+endif	# SND_AOA
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig
index d5fbd6016e9370ebd3493dc6e37f7658eacba3da..808eb11ebacdda3c44cb8ea04534681747a21e96 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -1,6 +1,5 @@
 config SND_AOA_ONYX
 	tristate "support Onyx chip"
-	depends on SND_AOA
 	select I2C
 	select I2C_POWERMAC
 	---help---
@@ -10,7 +9,6 @@ config SND_AOA_ONYX
 
 #config SND_AOA_TOPAZ
 #	tristate "support Topaz chips"
-#	depends on SND_AOA
 #	---help---
 #	This option enables support for the Topaz (CS84xx)
 #	codec chips found in the latest Apple machines,
@@ -19,7 +17,6 @@ config SND_AOA_ONYX
 
 config SND_AOA_TAS
 	tristate "support TAS chips"
-	depends on SND_AOA
 	select I2C
 	select I2C_POWERMAC
 	---help---
@@ -29,7 +26,6 @@ config SND_AOA_TAS
 
 config SND_AOA_TOONIE
 	tristate "support Toonie chip"
-	depends on SND_AOA
 	---help---
 	This option enables support for the toonie codec
 	found in the Mac Mini. If you have a Mac Mini and
diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig
index 50d7021ff677f5e1d578d83ff7ec3c760ea600c3..3ca475a886b1c1c0deda72295746ddf9a8ec31ea 100644
--- a/sound/aoa/fabrics/Kconfig
+++ b/sound/aoa/fabrics/Kconfig
@@ -1,6 +1,5 @@
 config SND_AOA_FABRIC_LAYOUT
 	tristate "layout-id fabric"
-	depends on SND_AOA
 	select SND_AOA_SOUNDBUS
 	select SND_AOA_SOUNDBUS_I2S
 	---help---
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index 7368b7ddfe0dd88f4f11f5973d8dfa186120c6d5..839d1137b9b23ca140574699c36e8ebaa8e960e6 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -1,6 +1,5 @@
 config SND_AOA_SOUNDBUS
 	tristate "Apple Soundbus support"
-	depends on SOUND
 	select SND_PCM
 	---help---
 	This option enables the generic driver for the soundbus
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 2e4a5e0d16db3726755dea8c8930f35138927395..351e19ea3785ce7159fe6957abc97a1b1c963971 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -1,11 +1,19 @@
 # ALSA ARM drivers
 
-menu "ALSA ARM devices"
-	depends on SND!=n && ARM
+menuconfig SND_ARM
+	bool "ARM sound devices"
+	depends on ARM
+	default y
+	help
+	  Support for sound devices specific to ARM architectures.
+	  Drivers that are implemented on ASoC can be found in
+	  "ALSA for SoC audio support" section.
+
+if SND_ARM
 
 config SND_SA11XX_UDA1341
 	tristate "SA11xx UDA1341TS driver (iPaq H3600)"
-	depends on ARCH_SA1100 && SND && L3
+	depends on ARCH_SA1100 && L3
 	select SND_PCM
 	help
 	  Say Y here if you have a Compaq iPaq H3x00 handheld computer
@@ -16,7 +24,7 @@ config SND_SA11XX_UDA1341
 
 config SND_ARMAACI
 	tristate "ARM PrimeCell PL041 AC Link support"
-	depends on SND && ARM_AMBA
+	depends on ARM_AMBA
 	select SND_PCM
 	select SND_AC97_CODEC
 
@@ -26,11 +34,12 @@ config SND_PXA2XX_PCM
 
 config SND_PXA2XX_AC97
 	tristate "AC97 driver for the Intel PXA2xx chip"
-	depends on ARCH_PXA && SND
+	depends on ARCH_PXA
 	select SND_PXA2XX_PCM
 	select SND_AC97_CODEC
 	help
 	  Say Y or M if you want to support any AC97 codec attached to
 	  the PXA2xx AC97 interface.
 
-endmenu
+endif	# SND_ARM
+
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
index 0eff33ca0f793742b97546deea5fa03b27dab220..faeddf3ecedb871835dbae30ddf74b07d85e42ad 100644
--- a/sound/arm/sa11xx-uda1341.c
+++ b/sound/arm/sa11xx-uda1341.c
@@ -21,8 +21,6 @@
  *                              merged HAL layer (patches from Brian)
  */
 
-/* $Id: sa11xx-uda1341.c,v 1.27 2005/12/07 09:13:42 cladisch Exp $ */
-
 /***************************************************************************************************
 *
 * To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index a8d71c6c8e7593f1619733bc7c9635bf118d43ca..335d45ecde6a19bc0d3f12a68363c669ea405a17 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -1,24 +1,19 @@
 # ALSA soundcard-configuration
 config SND_TIMER
 	tristate
-	depends on SND
 
 config SND_PCM
 	tristate
 	select SND_TIMER
-	depends on SND
 
 config SND_HWDEP
 	tristate
-	depends on SND
 
 config SND_RAWMIDI
 	tristate
-	depends on SND
 
 config SND_SEQUENCER
 	tristate "Sequencer support"
-	depends on SND
 	select SND_TIMER
 	help
 	  Say Y or M to enable MIDI sequencer and router support.  This
@@ -44,11 +39,9 @@ config SND_SEQ_DUMMY
 
 config SND_OSSEMUL
 	bool
-	depends on SND
 
 config SND_MIXER_OSS
 	tristate "OSS Mixer API"
-	depends on SND
 	select SND_OSSEMUL
 	help
 	  To enable OSS mixer API emulation (/dev/mixer*), say Y here
@@ -61,7 +54,6 @@ config SND_MIXER_OSS
 
 config SND_PCM_OSS
 	tristate "OSS PCM (digital audio) API"
-	depends on SND
 	select SND_OSSEMUL
 	select SND_PCM
 	help
@@ -84,7 +76,7 @@ config SND_PCM_OSS_PLUGINS
 
 config SND_SEQUENCER_OSS
 	bool "OSS Sequencer API"
-	depends on SND && SND_SEQUENCER
+	depends on SND_SEQUENCER
 	select SND_OSSEMUL
 	help
 	  Say Y here to enable OSS sequencer emulation (both
@@ -98,7 +90,7 @@ config SND_SEQUENCER_OSS
 
 config SND_RTCTIMER
 	tristate "RTC Timer support"
-	depends on SND && RTC
+	depends on RTC
 	select SND_TIMER
 	help
 	  Say Y here to enable RTC timer support for ALSA.  ALSA uses
@@ -123,7 +115,6 @@ config SND_SEQ_RTCTIMER_DEFAULT
 
 config SND_DYNAMIC_MINORS
 	bool "Dynamic device file minor numbers"
-	depends on SND
 	help
 	  If you say Y here, the minor numbers of ALSA device files in
 	  /dev/snd/ are allocated dynamically.  This allows you to have
@@ -134,7 +125,6 @@ config SND_DYNAMIC_MINORS
 
 config SND_SUPPORT_OLD_API
 	bool "Support old ALSA API"
-	depends on SND
 	default y
 	help
 	  Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
@@ -142,7 +132,7 @@ config SND_SUPPORT_OLD_API
 
 config SND_VERBOSE_PROCFS
 	bool "Verbose procfs contents"
-	depends on SND && PROC_FS
+	depends on PROC_FS
 	default y
 	help
 	  Say Y here to include code for verbose procfs contents (provides
@@ -151,7 +141,6 @@ config SND_VERBOSE_PROCFS
 
 config SND_VERBOSE_PRINTK
 	bool "Verbose printk"
-	depends on SND
 	help
 	  Say Y here to enable verbose log messages.  These messages
 	  will help to identify source file and position containing
@@ -161,16 +150,17 @@ config SND_VERBOSE_PRINTK
 
 config SND_DEBUG
 	bool "Debug"
-	depends on SND
 	help
 	  Say Y here to enable ALSA debug code.
 
-config SND_DEBUG_DETECT
-	bool "Debug detection"
+config SND_DEBUG_VERBOSE
+	bool "More verbose debug"
 	depends on SND_DEBUG
 	help
-	  Say Y here to enable extra-verbose log messages printed when
-	  detecting devices.
+	  Say Y here to enable extra-verbose debugging messages.
+	  
+	  Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages.
+	  So, say Y only if you are ready to be annoyed.
 
 config SND_PCM_XRUN_DEBUG
 	bool "Enable PCM ring buffer overrun/underrun debugging"
@@ -184,4 +174,3 @@ config SND_PCM_XRUN_DEBUG
 
 config SND_VMASTER
 	bool
-	depends on SND
diff --git a/sound/core/control.c b/sound/core/control.c
index 01a1a5af47bb7749e90370c7ff92dbae528b6199..281b2e2ef0eac0bb4b87c4a475433a466459a98d 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -684,7 +684,8 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
 	return result;
 }
 
-int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_read(struct snd_card *card,
+			     struct snd_ctl_elem_value *control)
 {
 	struct snd_kcontrol *kctl;
 	struct snd_kcontrol_volatile *vd;
@@ -734,8 +735,8 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
 	return result;
 }
 
-int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
-		       struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
+			      struct snd_ctl_elem_value *control)
 {
 	struct snd_kcontrol *kctl;
 	struct snd_kcontrol_volatile *vd;
diff --git a/sound/core/init.c b/sound/core/init.c
index ac0573416130d56b480caf2948a88f1017ad3bb8..5c254d498ae094d452bb0c85a143f468eaedde0a 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -46,17 +46,24 @@ static char *slots[SNDRV_CARDS];
 module_param_array(slots, charp, NULL, 0444);
 MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
 
-/* return non-zero if the given index is already reserved for another
+/* return non-zero if the given index is reserved for the given
  * module via slots option
  */
-static int module_slot_mismatch(struct module *module, int idx)
+static int module_slot_match(struct module *module, int idx)
 {
+	int match = 1;
 #ifdef MODULE
-	char *s1, *s2;
+	const char *s1, *s2;
+
 	if (!module || !module->name || !slots[idx])
 		return 0;
-	s1 = slots[idx];
-	s2 = module->name;
+
+	s1 = module->name;
+	s2 = slots[idx];
+	if (*s2 == '!') {
+		match = 0; /* negative match */
+		s2++;
+	}
 	/* compare module name strings
 	 * hyphens are handled as equivalent with underscore
 	 */
@@ -68,12 +75,12 @@ static int module_slot_mismatch(struct module *module, int idx)
 		if (c2 == '-')
 			c2 = '_';
 		if (c1 != c2)
-			return 1;
+			return !match;
 		if (!c1)
 			break;
 	}
-#endif
-	return 0;
+#endif /* MODULE */
+	return match;
 }
 
 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
@@ -129,7 +136,7 @@ struct snd_card *snd_card_new(int idx, const char *xid,
 			 struct module *module, int extra_size)
 {
 	struct snd_card *card;
-	int err;
+	int err, idx2;
 
 	if (extra_size < 0)
 		extra_size = 0;
@@ -144,35 +151,41 @@ struct snd_card *snd_card_new(int idx, const char *xid,
 	err = 0;
 	mutex_lock(&snd_card_mutex);
 	if (idx < 0) {
-		int idx2;
 		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
 			/* idx == -1 == 0xffff means: take any free slot */
 			if (~snd_cards_lock & idx & 1<<idx2) {
-				if (module_slot_mismatch(module, idx2))
-					continue;
-				idx = idx2;
-				if (idx >= snd_ecards_limit)
-					snd_ecards_limit = idx + 1;
-				break;
+				if (module_slot_match(module, idx2)) {
+					idx = idx2;
+					break;
+				}
+			}
+	}
+	if (idx < 0) {
+		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+			/* idx == -1 == 0xffff means: take any free slot */
+			if (~snd_cards_lock & idx & 1<<idx2) {
+				if (!slots[idx2] || !*slots[idx2]) {
+					idx = idx2;
+					break;
+				}
 			}
-	} else {
-		 if (idx < snd_ecards_limit) {
-			if (snd_cards_lock & (1 << idx))
-				err = -EBUSY;	/* invalid */
-		} else {
-			if (idx < SNDRV_CARDS)
-				snd_ecards_limit = idx + 1; /* increase the limit */
-			else
-				err = -ENODEV;
-		}
 	}
-	if (idx < 0 || err < 0) {
+	if (idx < 0)
+		err = -ENODEV;
+	else if (idx < snd_ecards_limit) {
+		if (snd_cards_lock & (1 << idx))
+			err = -EBUSY;	/* invalid */
+	} else if (idx >= SNDRV_CARDS)
+		err = -ENODEV;
+	if (err < 0) {
 		mutex_unlock(&snd_card_mutex);
 		snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",
 			 idx, snd_ecards_limit - 1, err);
 		goto __error;
 	}
 	snd_cards_lock |= 1 << idx;		/* lock it */
+	if (idx >= snd_ecards_limit)
+		snd_ecards_limit = idx + 1; /* increase the limit */
 	mutex_unlock(&snd_card_mutex);
 	card->number = idx;
 	card->module = module;
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 23b7bc02728b44ac9ad1086a827ffd3b917418ea..f5d6d8d129790d8729b82c1f7221616a7ea19f63 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -79,68 +79,6 @@ struct snd_mem_list {
 #define snd_assert(expr, args...) /**/
 #endif
 
-/*
- *  Hacks
- */
-
-#if defined(__i386__)
-/*
- * A hack to allocate large buffers via dma_alloc_coherent()
- *
- * since dma_alloc_coherent always tries GFP_DMA when the requested
- * pci memory region is below 32bit, it happens quite often that even
- * 2 order of pages cannot be allocated.
- *
- * so in the following, we allocate at first without dma_mask, so that
- * allocation will be done without GFP_DMA.  if the area doesn't match
- * with the requested region, then realloate with the original dma_mask
- * again.
- *
- * Really, we want to move this type of thing into dma_alloc_coherent()
- * so dma_mask doesn't have to be messed with.
- */
-
-static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
-					 dma_addr_t *dma_handle,
-					 gfp_t flags)
-{
-	void *ret;
-	u64 dma_mask, coherent_dma_mask;
-
-	if (dev == NULL || !dev->dma_mask)
-		return dma_alloc_coherent(dev, size, dma_handle, flags);
-	dma_mask = *dev->dma_mask;
-	coherent_dma_mask = dev->coherent_dma_mask;
-	*dev->dma_mask = 0xffffffff; 	/* do without masking */
-	dev->coherent_dma_mask = 0xffffffff; 	/* do without masking */
-	ret = dma_alloc_coherent(dev, size, dma_handle, flags);
-	*dev->dma_mask = dma_mask;	/* restore */
-	dev->coherent_dma_mask = coherent_dma_mask;	/* restore */
-	if (ret) {
-		/* obtained address is out of range? */
-		if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) {
-			/* reallocate with the proper mask */
-			dma_free_coherent(dev, size, ret, *dma_handle);
-			ret = dma_alloc_coherent(dev, size, dma_handle, flags);
-		}
-	} else {
-		/* wish to success now with the proper mask... */
-		if (dma_mask != 0xffffffffUL) {
-			/* allocation with GFP_ATOMIC to avoid the long stall */
-			flags &= ~GFP_KERNEL;
-			flags |= GFP_ATOMIC;
-			ret = dma_alloc_coherent(dev, size, dma_handle, flags);
-		}
-	}
-	return ret;
-}
-
-/* redefine dma_alloc_coherent for some architectures */
-#undef dma_alloc_coherent
-#define dma_alloc_coherent snd_dma_hack_alloc_coherent
-
-#endif /* arch */
-
 /*
  *
  *  Generic memory allocators
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 47cfa5186e34e15f4775f6c15172b136f90a3999..7a1545d2d9533f4ad53b6e1693969e34cb067965 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -148,7 +148,7 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
 		return NULL;
 	}
 	spin_unlock_irqrestore(&clients_lock, flags);
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 	if (!in_interrupt()) {
 		static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS];
 		static char card_requested[SNDRV_CARDS];
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 2f00ad28a2b73e1089df52642106aa2d4f82a8fe..05410e536a4f432c75289cdea5c1843a7bad88dd 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -124,7 +124,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
  * load all registered drivers (called from seq_clientmgr.c)
  */
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 /* avoid auto-loading during module_init() */
 static int snd_seq_in_init;
 void snd_seq_autoload_lock(void)
@@ -140,7 +140,7 @@ void snd_seq_autoload_unlock(void)
 
 void snd_seq_device_load_drivers(void)
 {
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 	struct ops_list *ops;
 
 	/* Calling request_module during module_init()
@@ -566,7 +566,5 @@ EXPORT_SYMBOL(snd_seq_device_load_drivers);
 EXPORT_SYMBOL(snd_seq_device_new);
 EXPORT_SYMBOL(snd_seq_device_register_driver);
 EXPORT_SYMBOL(snd_seq_device_unregister_driver);
-#ifdef CONFIG_KMOD
 EXPORT_SYMBOL(snd_seq_autoload_lock);
 EXPORT_SYMBOL(snd_seq_autoload_unlock);
-#endif
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 6c8ab48c689a6bb17a6b4dd490ba26c1204e32fd..09a94953745a3835569e199e29070610638dd0c6 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -60,14 +60,14 @@ EXPORT_SYMBOL(snd_ecards_limit);
 static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
 static DEFINE_MUTEX(sound_mutex);
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 
 /**
  * snd_request_card - try to load the card module
  * @card: the card number
  *
  * Tries to load the module "snd-card-X" for the given card number
- * via KMOD.  Returns immediately if already loaded.
+ * via request_module.  Returns immediately if already loaded.
  */
 void snd_request_card(int card)
 {
@@ -92,7 +92,7 @@ static void snd_request_other(int minor)
 	request_module(str);
 }
 
-#endif				/* request_module support */
+#endif	/* modular kernel */
 
 /**
  * snd_lookup_minor_data - get user data of a registered device
@@ -132,7 +132,7 @@ static int snd_open(struct inode *inode, struct file *file)
 		return -ENODEV;
 	mptr = snd_minors[minor];
 	if (mptr == NULL) {
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 		int dev = SNDRV_MINOR_DEVICE(minor);
 		if (dev == SNDRV_MINOR_CONTROL) {
 			/* /dev/aloadC? */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 9d8184a2c2d06350f44d294fdabd3d4797c6b638..0af337efc64e282367727e965bb36c090d0afd37 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -146,7 +146,7 @@ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
 	return NULL;
 }
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 
 static void snd_timer_request(struct snd_timer_id *tid)
 {
@@ -259,8 +259,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
 	/* open a master instance */
 	mutex_lock(&register_mutex);
 	timer = snd_timer_find(tid);
-#ifdef CONFIG_KMOD
-	if (timer == NULL) {
+#ifdef CONFIG_MODULES
+	if (!timer) {
 		mutex_unlock(&register_mutex);
 		snd_timer_request(tid);
 		mutex_lock(&register_mutex);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 602b58e3b55d198914f047c6d806871abfddd00e..255fd18b9aecc357730f16c1670706d5c5d95c1d 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -1,15 +1,41 @@
-# ALSA generic drivers
+config SND_MPU401_UART
+        tristate
+        select SND_RAWMIDI
 
-menu "Generic devices"
-	depends on SND!=n
+config SND_OPL3_LIB
+	tristate
+	select SND_TIMER
+	select SND_HWDEP
 
+config SND_OPL4_LIB
+	tristate
+	select SND_TIMER
+	select SND_HWDEP
+
+config SND_VX_LIB
+	tristate
+	select SND_HWDEP
+	select SND_PCM
+
+config SND_AC97_CODEC
+	tristate
+	select SND_PCM
+	select AC97_BUS
+	select SND_VMASTER
+
+menuconfig SND_DRIVERS
+	bool "Generic sound devices"
+	default y
+	help
+	  Support for generic sound devices.
+  
+if SND_DRIVERS
 
 config SND_PCSP
 	tristate "PC-Speaker support (READ HELP!)"
 	depends on PCSPKR_PLATFORM && X86_PC && HIGH_RES_TIMERS
 	depends on INPUT
 	depends on EXPERIMENTAL
-	depends on SND
 	select SND_PCM
 	help
 	  If you don't have a sound card in your computer, you can include a
@@ -35,33 +61,8 @@ config SND_PCSP
 	  Say M if you don't.
 	  Say Y only if you really know what you do.
 
-config SND_MPU401_UART
-        tristate
-        select SND_RAWMIDI
-
-config SND_OPL3_LIB
-	tristate
-	select SND_TIMER
-	select SND_HWDEP
-
-config SND_OPL4_LIB
-	tristate
-	select SND_TIMER
-	select SND_HWDEP
-
-config SND_VX_LIB
-	tristate
-	select SND_HWDEP
-	select SND_PCM
-
-config SND_AC97_CODEC
-	tristate
-	select SND_PCM
-	select AC97_BUS
-
 config SND_DUMMY
 	tristate "Dummy (/dev/null) soundcard"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include the dummy driver.  This driver does
@@ -90,7 +91,6 @@ config SND_VIRMIDI
 
 config SND_MTPAV
 	tristate "MOTU MidiTimePiece AV multiport MIDI"
-	depends on SND
 	select SND_RAWMIDI
 	help
 	  To use a MOTU MidiTimePiece AV multiport MIDI adapter
@@ -102,7 +102,7 @@ config SND_MTPAV
 
 config SND_MTS64
 	tristate "ESI Miditerminal 4140 driver"
-	depends on SND && PARPORT
+	depends on PARPORT
 	select SND_RAWMIDI
 	help
 	  The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with 
@@ -115,7 +115,6 @@ config SND_MTS64
 
 config SND_SERIAL_U16550
 	tristate "UART16550 serial MIDI driver"
-	depends on SND
 	select SND_RAWMIDI
 	help
 	  To include support for MIDI serial port interfaces, say Y here
@@ -131,7 +130,6 @@ config SND_SERIAL_U16550
 
 config SND_MPU401
 	tristate "Generic MPU-401 UART driver"
-	depends on SND
 	select SND_MPU401_UART
 	help
 	  Say Y here to include support for MIDI ports compatible with
@@ -142,7 +140,7 @@ config SND_MPU401
 
 config SND_PORTMAN2X4
 	tristate "Portman 2x4 driver"
-	depends on SND && PARPORT
+	depends on PARPORT
 	select SND_RAWMIDI
 	help
 	  Say Y here to include support for Midiman Portman 2x4 parallel
@@ -153,7 +151,7 @@ config SND_PORTMAN2X4
 
 config SND_ML403_AC97CR
 	tristate "Xilinx ML403 AC97 Controller Reference"
-	depends on SND && XILINX_VIRTEX
+	depends on XILINX_VIRTEX
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the
@@ -163,4 +161,25 @@ config SND_ML403_AC97CR
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-ml403_ac97cr.
 
-endmenu
+config SND_AC97_POWER_SAVE
+	bool "AC97 Power-Saving Mode"
+	depends on SND_AC97_CODEC && EXPERIMENTAL
+	default n
+	help
+	  Say Y here to enable the aggressive power-saving support of
+	  AC97 codecs.  In this mode, the power-mode is dynamically
+	  controlled at each open/close.
+
+	  The mode is activated by passing power_save=1 option to
+	  snd-ac97-codec driver.  You can toggle it dynamically over
+	  sysfs, too.
+
+config SND_AC97_POWER_SAVE_DEFAULT
+	int "Default time-out for AC97 power-save mode"
+	depends on SND_AC97_POWER_SAVE
+	default 0
+	help
+	  The default time-out value in seconds for AC97 automatic
+	  power-save mode.  0 means to disable the power-save mode.
+
+endif	# SND_DRIVERS
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index 1dfe6948e6ffdb14d36438dad0940e520f862338..efd22e92bcedea68f2a1de780f8e2b919b3d33fe 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -183,7 +183,7 @@ static int vx_hwdep_dsp_load(struct snd_hwdep *hw,
 		kfree(fw);
 		return -ENOMEM;
 	}
-	if (copy_from_user(fw->data, dsp->image, dsp->length)) {
+	if (copy_from_user((void *)fw->data, dsp->image, dsp->length)) {
 		free_fw(fw);
 		return -EFAULT;
 	}
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index e57e9cbe6a0f99aa36d8090a222c91b76b555771..9c3d361accfb108e71bf2ceea04a42f0b12972dd 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <asm/unaligned.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
@@ -264,10 +265,7 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
 		goto __fail;
 	}
 	/* write default channel status bytes */
-	buf[0] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0));
-	buf[1] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8));
-	buf[2] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16));
-	buf[3] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24));
+	put_unaligned_le32(SNDRV_PCM_DEFAULT_CON_SPDIF, buf);
 	memset(buf + 4, 0, 24 - 4);
 	if (snd_cs8427_send_corudata(device, 0, buf, 24) < 0)
 		goto __fail;
diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
index bfa5d2c3608bfed3d412165357a3889b4205d24e..1f4942ea141433dfef8569fa3c3f3e478688ffb7 100644
--- a/sound/i2c/l3/uda1341.c
+++ b/sound/i2c/l3/uda1341.c
@@ -17,8 +17,6 @@
  * 2002-05-12   Tomas Kasparek  another code cleanup
  */
 
-/* $Id: uda1341.c,v 1.18 2005/11/17 14:17:21 tiwai Exp $ */
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 2639a6ab8f2ea122425b1b8739cfad29ac73eb2c..25347a25d63cf7571a5db7bfb0309da0b1ac5181 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -21,12 +21,17 @@ config SND_SB16_DSP
         select SND_PCM
         select SND_SB_COMMON
 
-menu "ISA devices"
-	depends on SND!=n && ISA && ISA_DMA_API
+menuconfig SND_ISA
+	bool "ISA sound devices"
+	depends on ISA && ISA_DMA_API
+	default y
+	help
+	  Support for sound devices connected via the ISA bus.
+
+if SND_ISA
 
 config SND_ADLIB
 	tristate "AdLib FM card"
-	depends on SND
 	select SND_OPL3_LIB
 	help
 	  Say Y here to include support for AdLib FM cards.
@@ -36,7 +41,7 @@ config SND_ADLIB
 
 config SND_AD1816A
 	tristate "Analog Devices SoundPort AD1816A"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -50,7 +55,6 @@ config SND_AD1816A
 
 config SND_AD1848
 	tristate "Generic AD1848/CS4248 driver"
-	depends on SND
 	select SND_AD1848_LIB
 	help
 	  Say Y here to include support for AD1848 (Analog Devices) or
@@ -64,7 +68,7 @@ config SND_AD1848
 
 config SND_ALS100
 	tristate "Avance Logic ALS100/ALS120"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -78,7 +82,7 @@ config SND_ALS100
 
 config SND_AZT2320
 	tristate "Aztech Systems AZT2320"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -92,7 +96,6 @@ config SND_AZT2320
 
 config SND_CMI8330
 	tristate "C-Media CMI8330"
-	depends on SND
 	select SND_AD1848_LIB
 	select SND_SB16_DSP
 	help
@@ -104,7 +107,6 @@ config SND_CMI8330
 
 config SND_CS4231
 	tristate "Generic Cirrus Logic CS4231 driver"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
 	help
@@ -116,7 +118,6 @@ config SND_CS4231
 
 config SND_CS4232
 	tristate "Generic Cirrus Logic CS4232 driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -129,7 +130,6 @@ config SND_CS4232
 
 config SND_CS4236
 	tristate "Generic Cirrus Logic CS4236+ driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -142,7 +142,7 @@ config SND_CS4236
 
 config SND_DT019X
 	tristate "Diamond Technologies DT-019X, Avance Logic ALS-007"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -156,7 +156,7 @@ config SND_DT019X
 
 config SND_ES968
 	tristate "Generic ESS ES968 driver"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_MPU401_UART
 	select SND_SB8_DSP
@@ -168,7 +168,6 @@ config SND_ES968
 
 config SND_ES1688
 	tristate "Generic ESS ES688/ES1688 driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -181,7 +180,6 @@ config SND_ES1688
 
 config SND_ES18XX
 	tristate "Generic ESS ES18xx driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -193,7 +191,7 @@ config SND_ES18XX
 
 config SND_SC6000
 	tristate "Gallant SC-6000, Audio Excel DSP 16"
-	depends on SND && HAS_IOPORT
+	depends on HAS_IOPORT
 	select SND_AD1848_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -204,15 +202,10 @@ config SND_SC6000
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-sc6000.
 
-config SND_GUS_SYNTH
-	tristate
-
 config SND_GUSCLASSIC
 	tristate "Gravis UltraSound Classic"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_PCM
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for Gravis UltraSound Classic
 	  soundcards.
@@ -222,11 +215,9 @@ config SND_GUSCLASSIC
 
 config SND_GUSEXTREME
 	tristate "Gravis UltraSound Extreme"
-	depends on SND
 	select SND_HWDEP
 	select SND_MPU401_UART
 	select SND_PCM
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for Gravis UltraSound Extreme
 	  soundcards.
@@ -236,10 +227,8 @@ config SND_GUSEXTREME
 
 config SND_GUSMAX
 	tristate "Gravis UltraSound MAX"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_CS4231_LIB
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for Gravis UltraSound MAX
 	  soundcards.
@@ -249,10 +238,9 @@ config SND_GUSMAX
 
 config SND_INTERWAVE
 	tristate "AMD InterWave, Gravis UltraSound PnP"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select SND_RAWMIDI
 	select SND_CS4231_LIB
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for AMD InterWave based
 	  soundcards (Gravis UltraSound Plug & Play, STB SoundRage32,
@@ -263,10 +251,9 @@ config SND_INTERWAVE
 
 config SND_INTERWAVE_STB
 	tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select SND_RAWMIDI
 	select SND_CS4231_LIB
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for AMD InterWave based
 	  soundcards with a TEA6330T bass and treble regulator
@@ -277,7 +264,6 @@ config SND_INTERWAVE_STB
 
 config SND_OPL3SA2
 	tristate "Yamaha OPL3-SA2/SA3"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -290,7 +276,6 @@ config SND_OPL3SA2
 
 config SND_OPTI92X_AD1848
 	tristate "OPTi 82C92x - AD1848"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_OPL4_LIB
 	select SND_MPU401_UART
@@ -304,7 +289,6 @@ config SND_OPTI92X_AD1848
 
 config SND_OPTI92X_CS4231
 	tristate "OPTi 82C92x - CS4231"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_OPL4_LIB
 	select SND_MPU401_UART
@@ -318,10 +302,9 @@ config SND_OPTI92X_CS4231
 
 config SND_OPTI93X
 	tristate "OPTi 82C93x"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_CS4231_LIB
 	help
 	  Say Y here to include support for soundcards based on Opti
 	  82C93x chips.
@@ -331,7 +314,6 @@ config SND_OPTI93X
 
 config SND_MIRO
 	tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"
-	depends on SND
 	select SND_OPL4_LIB
 	select SND_CS4231_LIB
 	select SND_MPU401_UART
@@ -345,7 +327,6 @@ config SND_MIRO
 
 config SND_SB8
 	tristate "Sound Blaster 1.0/2.0/Pro (8-bit)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_RAWMIDI
 	select SND_SB8_DSP
@@ -358,7 +339,6 @@ config SND_SB8
 
 config SND_SB16
 	tristate "Sound Blaster 16 (PnP)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_SB16_DSP
@@ -371,7 +351,6 @@ config SND_SB16
 
 config SND_SBAWE
 	tristate "Sound Blaster AWE (32,64) (PnP)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_SB16_DSP
@@ -402,7 +381,6 @@ config SND_SB16_CSP_FIRMWARE_IN_KERNEL
 
 config SND_SGALAXY
 	tristate "Aztech Sound Galaxy"
-	depends on SND
 	select SND_AD1848_LIB
 	help
 	  Say Y here to include support for Aztech Sound Galaxy
@@ -413,7 +391,6 @@ config SND_SGALAXY
 
 config SND_SSCAPE
 	tristate "Ensoniq SoundScape PnP driver"
-	depends on SND
 	select SND_HWDEP
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -426,7 +403,6 @@ config SND_SSCAPE
 
 config SND_WAVEFRONT
 	tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)"
-	depends on SND
 	select FW_LOADER
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -448,4 +424,5 @@ config SND_WAVEFRONT_FIRMWARE_IN_KERNEL
 	  you need to install the firmware files from the
 	  alsa-firmware package.
 
-endmenu
+endif	# SND_ISA
+
diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
index 0aa8649e5c7f897b1c5fb283b69dcd5df5f56cfc..521db705d179f94a3edcb6963773448e0ded2f09 100644
--- a/sound/isa/cs423x/cs4231_lib.c
+++ b/sound/isa/cs423x/cs4231_lib.c
@@ -119,6 +119,42 @@ static unsigned char snd_cs4231_original_image[32] =
 	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
  */
@@ -895,7 +931,7 @@ static int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static void snd_cs4231_overrange(struct snd_cs4231 *chip)
+void snd_cs4231_overrange(struct snd_cs4231 *chip)
 {
 	unsigned long flags;
 	unsigned char res;
@@ -1054,8 +1090,11 @@ static int snd_cs4231_probe(struct snd_cs4231 *chip)
 	chip->image[CS4231_IFACE_CTRL] =
 	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
 	    (chip->single_dma ? CS4231_SINGLE_DMA : 0);
-	chip->image[CS4231_ALT_FEATURE_1] = 0x80;
-	chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
+	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);
@@ -1376,6 +1415,7 @@ const char *snd_cs4231_chip_id(struct snd_cs4231 *chip)
 	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 "???";
 	}
 }
@@ -1401,8 +1441,13 @@ static int snd_cs4231_new(struct snd_card *card,
 	chip->rate_constraint = snd_cs4231_xrate;
 	chip->set_playback_format = snd_cs4231_playback_format;
 	chip->set_capture_format = snd_cs4231_capture_format;
-        memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image));
-        
+	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;
 }
@@ -1790,6 +1835,48 @@ 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;
@@ -1802,10 +1889,22 @@ int snd_cs4231_mixer(struct snd_cs4231 *chip)
 
 	strcpy(card->mixername, chip->pcm->name);
 
-	for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
-		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0)
-			return err;
-	}
+	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;
 }
 
@@ -1815,6 +1914,7 @@ 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);
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index fe1afc13a01d4bdf6a373a40611f24e734788dcf..41c047e665ec7d62c6f85c25dbf45ce1441e901f 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -33,15 +33,10 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <sound/core.h>
-#ifdef CS4231
+#if defined(CS4231) || defined(OPTi93X)
 #include <sound/cs4231.h>
 #else
-#ifndef OPTi93X
 #include <sound/ad1848.h>
-#else
-#include <sound/control.h>
-#include <sound/pcm.h>
-#endif	/* OPTi93X */
 #endif	/* CS4231 */
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
@@ -109,7 +104,6 @@ module_param(dma2, int, 0444);
 MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
 #endif	/* CS4231 || OPTi93X */
 
-#define OPTi9XX_HW_DETECT	0
 #define OPTi9XX_HW_82C928	1
 #define OPTi9XX_HW_82C929	2
 #define OPTi9XX_HW_82C924	3
@@ -123,105 +117,12 @@ MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
 
 #ifdef OPTi93X
 
-#define OPTi93X_INDEX			0x00
-#define OPTi93X_DATA			0x01
 #define OPTi93X_STATUS			0x02
-#define OPTi93X_DDATA			0x03
 #define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
 
-#define OPTi93X_MIXOUT_LEFT		0x00
-#define OPTi93X_MIXOUT_RIGHT		0x01
-#define OPTi93X_CD_LEFT_INPUT		0x02
-#define OPTi93X_CD_RIGHT_INPUT		0x03
-#define OPTi930_AUX_LEFT_INPUT		0x04
-#define OPTi930_AUX_RIGHT_INPUT		0x05
-#define OPTi931_FM_LEFT_INPUT		0x04
-#define OPTi931_FM_RIGHT_INPUT		0x05
-#define OPTi93X_DAC_LEFT		0x06
-#define OPTi93X_DAC_RIGHT		0x07
-#define OPTi93X_PLAY_FORMAT		0x08
-#define OPTi93X_IFACE_CONF		0x09
-#define OPTi93X_PIN_CTRL		0x0a
-#define OPTi93X_ERR_INIT		0x0b
-#define OPTi93X_ID			0x0c
-#define OPTi93X_PLAY_UPR_CNT		0x0e
-#define OPTi93X_PLAY_LWR_CNT		0x0f
-#define OPTi931_AUX_LEFT_INPUT		0x10
-#define OPTi931_AUX_RIGHT_INPUT		0x11
-#define OPTi93X_LINE_LEFT_INPUT		0x12
-#define OPTi93X_LINE_RIGHT_INPUT	0x13
-#define OPTi93X_MIC_LEFT_INPUT		0x14
-#define OPTi93X_MIC_RIGHT_INPUT		0x15
-#define OPTi93X_OUT_LEFT		0x16
-#define OPTi93X_OUT_RIGHT		0x17
-#define OPTi93X_CAPT_FORMAT		0x1c
-#define OPTi93X_CAPT_UPR_CNT		0x1e
-#define OPTi93X_CAPT_LWR_CNT		0x1f
-
-#define OPTi93X_TRD			0x20
-#define OPTi93X_MCE			0x40
-#define OPTi93X_INIT			0x80
-
-#define OPTi93X_MIXOUT_MIC_GAIN		0x20
-#define OPTi93X_MIXOUT_LINE		0x00
-#define OPTi93X_MIXOUT_CD		0x40
-#define OPTi93X_MIXOUT_MIC		0x80
-#define OPTi93X_MIXOUT_MIXER		0xc0
-
-#define OPTi93X_STEREO			0x10
-#define OPTi93X_LINEAR_8		0x00
-#define OPTi93X_ULAW_8			0x20
-#define OPTi93X_LINEAR_16_LIT		0x40
-#define OPTi93X_ALAW_8			0x60
-#define OPTi93X_ADPCM_16		0xa0
-#define OPTi93X_LINEAR_16_BIG		0xc0
-
-#define OPTi93X_CAPTURE_PIO		0x80
-#define OPTi93X_PLAYBACK_PIO		0x40
-#define OPTi93X_AUTOCALIB		0x08
-#define OPTi93X_SINGLE_DMA		0x04
-#define OPTi93X_CAPTURE_ENABLE		0x02
-#define OPTi93X_PLAYBACK_ENABLE		0x01
-
-#define OPTi93X_IRQ_ENABLE		0x02
-
-#define OPTi93X_DMA_REQUEST		0x10
-#define OPTi93X_CALIB_IN_PROGRESS	0x20
-
 #define OPTi93X_IRQ_PLAYBACK		0x04
 #define OPTi93X_IRQ_CAPTURE		0x08
 
-
-struct snd_opti93x {
-	unsigned long port;
-	struct resource *res_port;
-	int irq;
-	int dma1;
-	int dma2;
-
-	struct snd_opti9xx *chip;
-	unsigned short hardware;
-	unsigned char image[32];
-
-	unsigned char mce_bit;
-	unsigned short mode;
-	int mute;
-
-	spinlock_t lock;
-
-	struct snd_card *card;
-	struct snd_pcm *pcm;
-	struct snd_pcm_substream *playback_substream;
-	struct snd_pcm_substream *capture_substream;
-	unsigned int p_dma_size;
-	unsigned int c_dma_size;
-};
-
-#define OPTi93X_MODE_NONE	0x00
-#define OPTi93X_MODE_PLAY	0x01
-#define OPTi93X_MODE_CAPTURE	0x02
-#define OPTi93X_MODE_OPEN	(OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE)
-
 #endif /* OPTi93X */
 
 struct snd_opti9xx {
@@ -234,6 +135,7 @@ struct snd_opti9xx {
 	unsigned long mc_base_size;
 #ifdef OPTi93X
 	unsigned long mc_indir_index;
+	struct snd_cs4231 *codec;
 #endif	/* OPTi93X */
 	unsigned long pwd_reg;
 
@@ -491,16 +393,9 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 		break;
 
 #else	/* OPTi93X */
-	case OPTi9XX_HW_82C930:
 	case OPTi9XX_HW_82C931:
 	case OPTi9XX_HW_82C933:
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
-			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
-			0x34);
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
-		/* 
+		/*
 		 * The BTC 1817DW has QS1000 wavetable which is connected
 		 * to the serial digital input of the OPTI931.
 		 */
@@ -510,6 +405,13 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
 		 * or digital input signal.
 		 */
 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
+	case OPTi9XX_HW_82C930: /* FALL THROUGH */
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
+			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
+			0x34);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
 		break;
 #endif	/* OPTi93X */
 
@@ -654,979 +556,23 @@ __skip_mpu:
 
 #ifdef OPTi93X
 
-static unsigned char snd_opti93x_default_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 */
-};
-
-
-static int snd_opti93x_busy_wait(struct snd_opti93x *chip)
-{
-	int timeout;
-
-	for (timeout = 250; timeout-- > 0; udelay(10))
-		if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT))
-			return 0;
-
-	snd_printk("chip still busy.\n");
-	return -EBUSY;
-}
-
-static unsigned char snd_opti93x_in(struct snd_opti93x *chip, unsigned char reg)
-{
-	snd_opti93x_busy_wait(chip);
-	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
-	return inb(OPTi93X_PORT(chip, DATA));
-}
-
-static void snd_opti93x_out(struct snd_opti93x *chip, unsigned char reg,
-			    unsigned char value)
-{
-	snd_opti93x_busy_wait(chip);
-	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
-	outb(value, OPTi93X_PORT(chip, DATA));
-}
-
-static void snd_opti93x_out_image(struct snd_opti93x *chip, unsigned char reg,
-				  unsigned char value)
-{
-	snd_opti93x_out(chip, reg, chip->image[reg] = value);
-}
-
-static void snd_opti93x_out_mask(struct snd_opti93x *chip, unsigned char reg,
-				 unsigned char mask, unsigned char value)
-{
-	snd_opti93x_out_image(chip, reg,
-		(chip->image[reg] & ~mask) | (value & mask));
-}
-
-
-static void snd_opti93x_mce_up(struct snd_opti93x *chip)
-{
-	snd_opti93x_busy_wait(chip);
-
-	chip->mce_bit = OPTi93X_MCE;
-	if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE))
-		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
-}
-
-static void snd_opti93x_mce_down(struct snd_opti93x *chip)
-{
-	snd_opti93x_busy_wait(chip);
-
-	chip->mce_bit = 0;
-	if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)
-		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
-}
-
-#define snd_opti93x_mute_reg(chip, reg, mute)	\
-	snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]);
-
-static void snd_opti93x_mute(struct snd_opti93x *chip, int mute)
-{
-	mute = mute ? 1 : 0;
-	if (chip->mute == mute)
-		return;
-
-	chip->mute = mute;
-
-	snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute);
-	switch (chip->hardware) {
-	case OPTi9XX_HW_82C930:
-		snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute);
-		break;
-	case OPTi9XX_HW_82C931:
-	case OPTi9XX_HW_82C933:
-		snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute);
-	}
-	snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute);
-}
-
-
-static unsigned int snd_opti93x_get_count(unsigned char format,
-					  unsigned int size)
-{
-	switch (format & 0xe0) {
-	case OPTi93X_LINEAR_16_LIT:
-	case OPTi93X_LINEAR_16_BIG:
-		size >>= 1;
-		break;
-	case OPTi93X_ADPCM_16:
-		return size >> 2;
-	}
-	return (format & OPTi93X_STEREO) ? (size >> 1) : size;
-}
-
-static unsigned int rates[] = {  5512,  6615,  8000,  9600, 11025, 16000, 
-				18900, 22050, 27428, 32000, 33075, 37800,
-				44100, 48000 };
-#define RATES ARRAY_SIZE(rates)
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
-	.count = RATES,
-	.list = rates,
-	.mask = 0,
-};
-
-static unsigned char bits[] = {  0x01,  0x0f,  0x00,  0x0e,  0x03,  0x02,
-				 0x05,  0x07,  0x04,  0x06,  0x0d,  0x09,
-				 0x0b,  0x0c};
-
-static unsigned char snd_opti93x_get_freq(unsigned int rate)
-{
-	unsigned int i;
-
-	for (i = 0; i < RATES; i++) {
-		if (rate == rates[i])
-			return bits[i];
-	}
-	snd_BUG();
-	return bits[RATES-1];
-}
-
-static unsigned char snd_opti93x_get_format(struct snd_opti93x *chip,
-					    unsigned int format, int channels)
-{
-	unsigned char retval = OPTi93X_LINEAR_8;
-
-	switch (format) {
-	case SNDRV_PCM_FORMAT_MU_LAW:
-		retval = OPTi93X_ULAW_8;
-		break;
-	case SNDRV_PCM_FORMAT_A_LAW:
-		retval = OPTi93X_ALAW_8;
-		break;
-	case SNDRV_PCM_FORMAT_S16_LE:
-		retval = OPTi93X_LINEAR_16_LIT;
-		break;
-	case SNDRV_PCM_FORMAT_S16_BE:
-		retval = OPTi93X_LINEAR_16_BIG;
-		break;
-	case SNDRV_PCM_FORMAT_IMA_ADPCM:
-		retval = OPTi93X_ADPCM_16;
-	}
-	return (channels > 1) ? (retval | OPTi93X_STEREO) : retval;
-}
-
-
-static void snd_opti93x_playback_format(struct snd_opti93x *chip, unsigned char fmt)
-{
-	unsigned char mask;
-
-	snd_opti93x_mute(chip, 1);
-
-	snd_opti93x_mce_up(chip);
-	mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff;
-	snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt);
-	snd_opti93x_mce_down(chip);
-
-	snd_opti93x_mute(chip, 0);
-}
-
-static void snd_opti93x_capture_format(struct snd_opti93x *chip, unsigned char fmt)
-{
-	snd_opti93x_mute(chip, 1);
-
-	snd_opti93x_mce_up(chip);
-	if (!(chip->mode & OPTi93X_MODE_PLAY))
-		snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt);
-	else
-		fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0;
-	snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt);
-	snd_opti93x_mce_down(chip);
-
-	snd_opti93x_mute(chip, 0);
-}
-
-
-static int snd_opti93x_open(struct snd_opti93x *chip, unsigned int mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	if (chip->mode & mode) {
-		spin_unlock_irqrestore(&chip->lock, flags);
-		return -EAGAIN;
-	}
-
-	if (!(chip->mode & OPTi93X_MODE_OPEN)) {
-		outb(0x00, OPTi93X_PORT(chip, STATUS));
-		snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL,
-			OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE);
-		chip->mode = mode;
-	}
-	else
-		chip->mode |= mode;
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static void snd_opti93x_close(struct snd_opti93x *chip, unsigned int mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	chip->mode &= ~mode;
-	if (chip->mode & OPTi93X_MODE_OPEN) {
-		spin_unlock_irqrestore(&chip->lock, flags);
-		return;
-	}
-
-	snd_opti93x_mute(chip, 1);
-
-	outb(0, OPTi93X_PORT(chip, STATUS));
-	snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE,
-		~OPTi93X_IRQ_ENABLE);
-
-	snd_opti93x_mce_up(chip);
-	snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00);
-	snd_opti93x_mce_down(chip);
-	chip->mode = 0;
-
-	snd_opti93x_mute(chip, 0);
-	spin_unlock_irqrestore(&chip->lock, flags);
-}
-
-static int snd_opti93x_trigger(struct snd_pcm_substream *substream, 
-			       unsigned char what, int cmd)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_STOP:
-	{
-		unsigned int what = 0;
-		struct snd_pcm_substream *s;
-		snd_pcm_group_for_each_entry(s, substream) {
-			if (s == chip->playback_substream) {
-				what |= OPTi93X_PLAYBACK_ENABLE;
-				snd_pcm_trigger_done(s, substream);
-			} else if (s == chip->capture_substream) {
-				what |= OPTi93X_CAPTURE_ENABLE;
-				snd_pcm_trigger_done(s, substream);
-			}
-		}
-		spin_lock(&chip->lock);
-		if (cmd == SNDRV_PCM_TRIGGER_START) {
-			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what);
-			if (what & OPTi93X_CAPTURE_ENABLE)
-				udelay(50);
-		} else
-			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00);
-		spin_unlock(&chip->lock);
-		break;
-	}
-	default:
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int snd_opti93x_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	return snd_opti93x_trigger(substream,
-				   OPTi93X_PLAYBACK_ENABLE, cmd);
-}
-
-static int snd_opti93x_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	return snd_opti93x_trigger(substream,
-				   OPTi93X_CAPTURE_ENABLE, cmd);
-}
-
-static int snd_opti93x_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *hw_params)
-{
-	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-
-static int snd_opti93x_hw_free(struct snd_pcm_substream *substream)
-{
-	snd_pcm_lib_free_pages(substream);
-	return 0;
-}
-
-
-static int snd_opti93x_playback_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned char format;
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	chip->p_dma_size = size;
-	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
-		OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO,
-		~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO));
-
-	snd_dma_program(chip->dma1, runtime->dma_addr, size,
-		DMA_MODE_WRITE | DMA_AUTOINIT);
-
-	format = snd_opti93x_get_freq(runtime->rate);
-	format |= snd_opti93x_get_format(chip, runtime->format,
-		runtime->channels);
-	snd_opti93x_playback_format(chip, format);
-	format = chip->image[OPTi93X_PLAY_FORMAT];
-
-	count = snd_opti93x_get_count(format, count) - 1;
-	snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count);
-	snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8);
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static int snd_opti93x_capture_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned char format;
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	chip->c_dma_size = size;
-	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
-		OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, 0);
-
-	snd_dma_program(chip->dma2, runtime->dma_addr, size,
-		DMA_MODE_READ | DMA_AUTOINIT);
-
-	format = snd_opti93x_get_freq(runtime->rate);
-	format |= snd_opti93x_get_format(chip, runtime->format,
-		runtime->channels);
-	snd_opti93x_capture_format(chip, format);
-	format = chip->image[OPTi93X_CAPT_FORMAT];
-
-	count = snd_opti93x_get_count(format, count) - 1;
-	snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count);
-	snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8);
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static snd_pcm_uframes_t snd_opti93x_playback_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_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_opti93x_capture_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-	
-	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE))
-		return 0;
-
-	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-
-static void snd_opti93x_overrange(struct snd_opti93x *chip)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02))
-		chip->capture_substream->runtime->overrange++;
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-}
-
 static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
 {
-	struct snd_opti93x *codec = dev_id;
+	struct snd_cs4231 *codec = dev_id;
+	struct snd_opti9xx *chip = codec->card->private_data;
 	unsigned char status;
 
-	status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11));
+	status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
 	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
 		snd_pcm_period_elapsed(codec->playback_substream);
 	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
-		snd_opti93x_overrange(codec);
+		snd_cs4231_overrange(codec);
 		snd_pcm_period_elapsed(codec->capture_substream);
 	}
 	outb(0x00, OPTi93X_PORT(codec, STATUS));
 	return IRQ_HANDLED;
 }
 
-
-static struct snd_pcm_hardware snd_opti93x_playback = {
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID | 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 =		5512,
-	.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_opti93x_capture = {
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID | 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_8000_48000,
-	.rate_min =		5512,
-	.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_opti93x_playback_open(struct snd_pcm_substream *substream)
-{
-	int error;
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-
-	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0)
-		return error;
-	snd_pcm_set_sync(substream);
-	chip->playback_substream = substream;
-	runtime->hw = snd_opti93x_playback;
-	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-	return error;
-}
-
-static int snd_opti93x_capture_open(struct snd_pcm_substream *substream)
-{
-	int error;
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-
-	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0)
-		return error;
-	runtime->hw = snd_opti93x_capture;
-	snd_pcm_set_sync(substream);
-	chip->capture_substream = substream;
-	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-	return error;
-}
-
-static int snd_opti93x_playback_close(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
-	chip->playback_substream = NULL;
-	snd_opti93x_close(chip, OPTi93X_MODE_PLAY);
-	return 0;
-}
-
-static int snd_opti93x_capture_close(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
-	chip->capture_substream = NULL;
-	snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE);
-	return 0;
-}
-
-
-static void snd_opti93x_init(struct snd_opti93x *chip)
-{
-	unsigned long flags;
-	int i;
-
-	spin_lock_irqsave(&chip->lock, flags);
-	snd_opti93x_mce_up(chip);
-
-	for (i = 0; i < 32; i++)
-		snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]);
-
-	snd_opti93x_mce_down(chip);
-	spin_unlock_irqrestore(&chip->lock, flags);
-}
-
-static int snd_opti93x_probe(struct snd_opti93x *chip)
-{
-	unsigned long flags;
-	unsigned char val;
-
-	spin_lock_irqsave(&chip->lock, flags);
-	val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f;
-	spin_unlock_irqrestore(&chip->lock, flags);
-
-	return (val == 0x0a) ? 0 : -ENODEV;
-}
-
-static int snd_opti93x_free(struct snd_opti93x *chip)
-{
-	release_and_free_resource(chip->res_port);
-	if (chip->dma1 >= 0) {
-		disable_dma(chip->dma1);
-		free_dma(chip->dma1);
-	}
-	if (chip->dma2 >= 0) {
-		disable_dma(chip->dma2);
-		free_dma(chip->dma2);
-	}
-	if (chip->irq >= 0) {
-	  free_irq(chip->irq, chip);
-	}
-	kfree(chip);
-	return 0;
-}
-
-static int snd_opti93x_dev_free(struct snd_device *device)
-{
-	struct snd_opti93x *chip = device->device_data;
-	return snd_opti93x_free(chip);
-}
-
-static const char *snd_opti93x_chip_id(struct snd_opti93x *codec)
-{
-	switch (codec->hardware) {
-	case OPTi9XX_HW_82C930: return "82C930";
-	case OPTi9XX_HW_82C931: return "82C931";
-	case OPTi9XX_HW_82C933: return "82C933";
-	default:		return "???";
-	}
-}
-
-static int snd_opti93x_create(struct snd_card *card, struct snd_opti9xx *chip,
-			      int dma1, int dma2,
-			      struct snd_opti93x **rcodec)
-{
-	static struct snd_device_ops ops = {
-		.dev_free =	snd_opti93x_dev_free,
-	};
-	int error;
-	struct snd_opti93x *codec;
-
-	*rcodec = NULL;
-	codec = kzalloc(sizeof(*codec), GFP_KERNEL);
-	if (codec == NULL)
-		return -ENOMEM;
-	codec->irq = -1;
-	codec->dma1 = -1;
-	codec->dma2 = -1;
-
-	if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) {
-		snd_printk(KERN_ERR "opti9xx: can't grab port 0x%lx\n", chip->wss_base + 4);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-	if (request_dma(dma1, "OPTI93x - 1")) {
-		snd_printk(KERN_ERR "opti9xx: can't grab DMA1 %d\n", dma1);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-	codec->dma1 = chip->dma1;
-	if (request_dma(dma2, "OPTI93x - 2")) {
-		snd_printk(KERN_ERR "opti9xx: can't grab DMA2 %d\n", dma2);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-	codec->dma2 = chip->dma2;
-
-	if (request_irq(chip->irq, snd_opti93x_interrupt, IRQF_DISABLED, DEV_NAME" - WSS", codec)) {
-		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-
-	codec->card = card;
-	codec->port = chip->wss_base + 4;
-	codec->irq = chip->irq;
-
-	spin_lock_init(&codec->lock);
-	codec->hardware = chip->hardware;
-	codec->chip = chip;
-
-	if ((error = snd_opti93x_probe(codec))) {
-		snd_opti93x_free(codec);
-		return error;
-	}
-
-	snd_opti93x_init(codec);
-
-	/* Register device */
-	if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) {
-		snd_opti93x_free(codec);
-		return error;
-	}
-
-	*rcodec = codec;
-	return 0;
-}
-
-static struct snd_pcm_ops snd_opti93x_playback_ops = {
-	.open =		snd_opti93x_playback_open,
-	.close =	snd_opti93x_playback_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_opti93x_hw_params,
-	.hw_free =	snd_opti93x_hw_free,
-	.prepare =	snd_opti93x_playback_prepare,
-	.trigger =	snd_opti93x_playback_trigger,
-	.pointer =	snd_opti93x_playback_pointer,
-};
-
-static struct snd_pcm_ops snd_opti93x_capture_ops = {
-	.open =		snd_opti93x_capture_open,
-	.close =	snd_opti93x_capture_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_opti93x_hw_params,
-	.hw_free =	snd_opti93x_hw_free,
-	.prepare =	snd_opti93x_capture_prepare,
-	.trigger =	snd_opti93x_capture_trigger,
-	.pointer =	snd_opti93x_capture_pointer,
-};
-
-static int snd_opti93x_pcm(struct snd_opti93x *codec, int device, struct snd_pcm **rpcm)
-{
-	int error;
-	struct snd_pcm *pcm;
-
-	if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm)) < 0)
-		return error;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops);
-
-	pcm->private_data = codec;
-	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
-
-	strcpy(pcm->name, snd_opti93x_chip_id(codec));
-
-	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-					      snd_dma_isa_data(),
-					      64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024);
-
-	codec->pcm = pcm;
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-/*
- *  MIXER part
- */
-
-static int snd_opti93x_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	static char *texts[4] = {
-		"Line1", "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_opti93x_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	
-	spin_lock_irqsave(&chip->lock, flags);
-	ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6;
-	ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6;
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static int snd_opti93x_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *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->lock, flags);
-	left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left;
-	right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right;
-	change = left != chip->image[OPTi93X_MIXOUT_LEFT] ||
-	         right != chip->image[OPTi93X_MIXOUT_RIGHT];
-	snd_opti93x_out_image(chip, OPTi93X_MIXOUT_LEFT, left);
-	snd_opti93x_out_image(chip, OPTi93X_MIXOUT_RIGHT, right);
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return change;
-}
-
-#if 0
-
-#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
-  .info = snd_opti93x_info_single, \
-  .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \
-  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
-
-static int snd_opti93x_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_opti93x_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *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->lock, flags);
-	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
-	spin_unlock_irqrestore(&chip->lock, flags);
-	if (invert)
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-	return 0;
-}
-
-static int snd_opti93x_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *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->lock, flags);
-	val = (chip->image[reg] & ~(mask << shift)) | val;
-	change = val != chip->image[reg];
-	snd_opti93x_out(chip, reg, val);
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return change;
-}
-
-#endif /* single */
-
-#define OPTi93X_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_opti93x_info_double, \
-  .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \
-  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
-
-#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \
-	do { xctl.private_value ^= 22; } while (0)
-#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \
-	do { xctl.private_value &= ~0x0000ffff; \
-	     xctl.private_value |= left_reg | (right_reg << 8); } while (0)
-
-static int snd_opti93x_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_opti93x_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *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->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->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_opti93x_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *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->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_opti93x_out_image(chip, left_reg, val1);
-	snd_opti93x_out_image(chip, right_reg, val2);
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return change;
-}
-
-static struct snd_kcontrol_new snd_opti93x_controls[] __devinitdata = {
-OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), 
-OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 1),
-OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 1, 1, 15, 1), 
-OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), 
-OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1),
-OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), 
-OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0),
-{
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Source",
-	.info = snd_opti93x_info_mux,
-	.get = snd_opti93x_get_mux,
-	.put = snd_opti93x_put_mux,
-}
-};
-                                        
-static int __devinit snd_opti93x_mixer(struct snd_opti93x *chip)
-{
-	struct snd_card *card;
-	struct snd_kcontrol_new knew;
-	int err;
-	unsigned int idx;
-
-	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
-
-	card = chip->card;
-
-	strcpy(card->mixername, snd_opti93x_chip_id(chip));
-
-	for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
-		knew = snd_opti93x_controls[idx];
-		if (chip->hardware == OPTi9XX_HW_82C930) {
-			if (strstr(knew.name, "FM"))	/* skip FM controls */
-				continue;
-			else if (strcmp(knew.name, "Mic Playback Volume"))
-				OPTi93X_DOUBLE_INVERT_INVERT(knew);
-			else if (strstr(knew.name, "Aux"))
-				OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT);
-			else if (strcmp(knew.name, "PCM Playback Volume"))
-				OPTi93X_DOUBLE_INVERT_INVERT(knew);
-			else if (strcmp(knew.name, "Master Playback Volume"))
-				OPTi93X_DOUBLE_INVERT_INVERT(knew);
-		}
-		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0)
-			return err;
-	}
-	return 0;
-}
-
 #endif /* OPTi93X */
 
 static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
@@ -1739,8 +685,16 @@ static void snd_card_opti9xx_free(struct snd_card *card)
 {
 	struct snd_opti9xx *chip = card->private_data;
         
-	if (chip)
+	if (chip) {
+#ifdef OPTi93X
+		struct snd_cs4231 *codec = chip->codec;
+		if (codec->irq > 0) {
+			disable_irq(codec->irq);
+			free_irq(codec->irq, codec);
+		}
+#endif
 		release_and_free_resource(chip->res_mc_base);
+	}
 }
 
 static int __devinit snd_opti9xx_probe(struct snd_card *card)
@@ -1748,11 +702,11 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
 	int error;
 	struct snd_opti9xx *chip = card->private_data;
-#if defined(OPTi93X)
-	struct snd_opti93x *codec;
-#elif defined(CS4231)
+#if defined(CS4231) || defined(OPTi93X)
 	struct snd_cs4231 *codec;
+#ifdef CS4231
 	struct snd_timer *timer;
+#endif
 #else
 	struct snd_ad1848 *codec;
 #endif
@@ -1784,26 +738,34 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 	if ((error = snd_opti9xx_configure(chip)))
 		return error;
 
-#if defined(OPTi93X)
-	if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec)))
-		return error;
-	if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0)
-		return error;
-	if ((error = snd_opti93x_mixer(codec)) < 0)
-		return error;
-#elif defined(CS4231)
+#if defined(CS4231) || defined(OPTi93X)
 	if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
 				       chip->irq, chip->dma1, chip->dma2,
-				       CS4231_HW_DETECT,
-				       0,
+#ifdef CS4231
+				       CS4231_HW_DETECT, 0,
+#else /* OPTi93x */
+				       CS4231_HW_OPTI93X, CS4231_HWSHARE_IRQ,
+#endif
 				       &codec)) < 0)
 		return error;
+#ifdef OPTi93X
+	chip->codec = codec;
+#endif
 	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0)
 		return error;
 	if ((error = snd_cs4231_mixer(codec)) < 0)
 		return error;
+#ifdef CS4231
 	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0)
 		return error;
+#else /* OPTI93X */
+	error = request_irq(chip->irq, snd_opti93x_interrupt,
+			    IRQF_DISABLED, DEV_NAME" - WSS", codec);
+	if (error < 0) {
+		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
+		return error;
+	}
+#endif
 #else
 	if ((error = snd_ad1848_create(card, chip->wss_base + 4,
 				       chip->irq, chip->dma1,
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index c9d1c986d70e753f784235c26a806307a1af3598..1098a56b2f4b878898aca659d3096eafa6fd3edc 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -34,5 +34,3 @@ ifeq ($(CONFIG_SND_SB16_CSP),y)
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
 endif
 obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o
-
-obj-m := $(sort $(obj-m))
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 95eeca163354b5303b89554bdeafb8053bc54fcd..0bb9b92566010875f148106c8423bf04a5b3cff8 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1939,7 +1939,7 @@ static int __devinit
 wavefront_download_firmware (snd_wavefront_t *dev, char *path)
 
 {
-	unsigned char *buf;
+	const unsigned char *buf;
 	int len, err;
 	int section_cnt_downloaded = 0;
 	const struct firmware *firmware;
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index 531f8ba96a71ae40d05b810cc6b42e36fe2ec165..a9823fad85c2d9ecaf0bbc2bfd0ad13d3e254796 100644
--- a/sound/mips/Kconfig
+++ b/sound/mips/Kconfig
@@ -1,15 +1,34 @@
 # ALSA MIPS drivers
 
-menu "ALSA MIPS devices"
-	depends on SND!=n && MIPS
+menuconfig SND_MIPS
+	bool "MIPS sound devices"
+	depends on MIPS
+	default y
+	help
+	  Support for sound devices of MIPS architectures.
+
+if SND_MIPS
+
+config SND_SGI_O2
+	tristate "SGI O2 Audio"
+	depends on SGI_IP32
+        help
+                Sound support for the SGI O2 Workstation. 
+
+config SND_SGI_HAL2
+        tristate "SGI HAL2 Audio"
+        depends on SGI_HAS_HAL2
+        help
+                Sound support for the SGI Indy and Indigo2 Workstation.
+
 
 config SND_AU1X00
 	tristate "Au1x00 AC97 Port Driver"
-	depends on (SOC_AU1000 || SOC_AU1100 || SOC_AU1500) && SND
+	depends on SOC_AU1000 || SOC_AU1100 || SOC_AU1500
 	select SND_PCM
 	select SND_AC97_CODEC
 	help
 	  ALSA Sound driver for the Au1x00's AC97 port.
 
-endmenu
+endif	# SND_MIPS
 
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 47afed971fba6f81c7f06b841200f597e0794474..861ec0a574b46efd5315878f4666edc87d288271 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -3,6 +3,10 @@
 #
 
 snd-au1x00-objs := au1x00.o
+snd-sgi-o2-objs := sgio2audio.o ad1843.o
+snd-sgi-hal2-objs := hal2.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
+obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
+obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c
new file mode 100644
index 0000000000000000000000000000000000000000..c624510ec37476eccfec30d0e5c48ac2ec9647be
--- /dev/null
+++ b/sound/mips/ad1843.c
@@ -0,0 +1,561 @@
+/*
+ *   AD1843 low level driver
+ *
+ *   Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ *   Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ *
+ *   inspired from vwsnd.c (SGI VW audio driver)
+ *     Copyright 1999 Silicon Graphics, Inc.  All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ad1843.h>
+
+/*
+ * AD1843 bitfield definitions.  All are named as in the AD1843 data
+ * sheet, with ad1843_ prepended and individual bit numbers removed.
+ *
+ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
+ *
+ * Only the bitfields we need are defined.
+ */
+
+struct ad1843_bitfield {
+	char reg;
+	char lo_bit;
+	char nbits;
+};
+
+static const struct ad1843_bitfield
+	ad1843_PDNO   = {  0, 14,  1 },	/* Converter Power-Down Flag */
+	ad1843_INIT   = {  0, 15,  1 },	/* Clock Initialization Flag */
+	ad1843_RIG    = {  2,  0,  4 },	/* Right ADC Input Gain */
+	ad1843_RMGE   = {  2,  4,  1 },	/* Right ADC Mic Gain Enable */
+	ad1843_RSS    = {  2,  5,  3 },	/* Right ADC Source Select */
+	ad1843_LIG    = {  2,  8,  4 },	/* Left ADC Input Gain */
+	ad1843_LMGE   = {  2, 12,  1 },	/* Left ADC Mic Gain Enable */
+	ad1843_LSS    = {  2, 13,  3 },	/* Left ADC Source Select */
+	ad1843_RD2M   = {  3,  0,  5 },	/* Right DAC 2 Mix Gain/Atten */
+	ad1843_RD2MM  = {  3,  7,  1 },	/* Right DAC 2 Mix Mute */
+	ad1843_LD2M   = {  3,  8,  5 },	/* Left DAC 2 Mix Gain/Atten */
+	ad1843_LD2MM  = {  3, 15,  1 },	/* Left DAC 2 Mix Mute */
+	ad1843_RX1M   = {  4,  0,  5 },	/* Right Aux 1 Mix Gain/Atten */
+	ad1843_RX1MM  = {  4,  7,  1 },	/* Right Aux 1 Mix Mute */
+	ad1843_LX1M   = {  4,  8,  5 },	/* Left Aux 1 Mix Gain/Atten */
+	ad1843_LX1MM  = {  4, 15,  1 },	/* Left Aux 1 Mix Mute */
+	ad1843_RX2M   = {  5,  0,  5 },	/* Right Aux 2 Mix Gain/Atten */
+	ad1843_RX2MM  = {  5,  7,  1 },	/* Right Aux 2 Mix Mute */
+	ad1843_LX2M   = {  5,  8,  5 },	/* Left Aux 2 Mix Gain/Atten */
+	ad1843_LX2MM  = {  5, 15,  1 },	/* Left Aux 2 Mix Mute */
+	ad1843_RMCM   = {  7,  0,  5 },	/* Right Mic Mix Gain/Atten */
+	ad1843_RMCMM  = {  7,  7,  1 },	/* Right Mic Mix Mute */
+	ad1843_LMCM   = {  7,  8,  5 },	/* Left Mic Mix Gain/Atten */
+	ad1843_LMCMM  = {  7, 15,  1 },	/* Left Mic Mix Mute */
+	ad1843_HPOS   = {  8,  4,  1 },	/* Headphone Output Voltage Swing */
+	ad1843_HPOM   = {  8,  5,  1 },	/* Headphone Output Mute */
+	ad1843_MPOM   = {  8,  6,  1 },	/* Mono Output Mute */
+	ad1843_RDA1G  = {  9,  0,  6 },	/* Right DAC1 Analog/Digital Gain */
+	ad1843_RDA1GM = {  9,  7,  1 },	/* Right DAC1 Analog Mute */
+	ad1843_LDA1G  = {  9,  8,  6 },	/* Left DAC1 Analog/Digital Gain */
+	ad1843_LDA1GM = {  9, 15,  1 },	/* Left DAC1 Analog Mute */
+	ad1843_RDA2G  = { 10,  0,  6 },	/* Right DAC2 Analog/Digital Gain */
+	ad1843_RDA2GM = { 10,  7,  1 },	/* Right DAC2 Analog Mute */
+	ad1843_LDA2G  = { 10,  8,  6 },	/* Left DAC2 Analog/Digital Gain */
+	ad1843_LDA2GM = { 10, 15,  1 },	/* Left DAC2 Analog Mute */
+	ad1843_RDA1AM = { 11,  7,  1 },	/* Right DAC1 Digital Mute */
+	ad1843_LDA1AM = { 11, 15,  1 },	/* Left DAC1 Digital Mute */
+	ad1843_RDA2AM = { 12,  7,  1 },	/* Right DAC2 Digital Mute */
+	ad1843_LDA2AM = { 12, 15,  1 },	/* Left DAC2 Digital Mute */
+	ad1843_ADLC   = { 15,  0,  2 },	/* ADC Left Sample Rate Source */
+	ad1843_ADRC   = { 15,  2,  2 },	/* ADC Right Sample Rate Source */
+	ad1843_DA1C   = { 15,  8,  2 },	/* DAC1 Sample Rate Source */
+	ad1843_DA2C   = { 15, 10,  2 },	/* DAC2 Sample Rate Source */
+	ad1843_C1C    = { 17,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_C2C    = { 20,  0, 16 },	/* Clock 2 Sample Rate Select */
+	ad1843_C3C    = { 23,  0, 16 },	/* Clock 3 Sample Rate Select */
+	ad1843_DAADL  = { 25,  4,  2 },	/* Digital ADC Left Source Select */
+	ad1843_DAADR  = { 25,  6,  2 },	/* Digital ADC Right Source Select */
+	ad1843_DAMIX  = { 25, 14,  1 },	/* DAC Digital Mix Enable */
+	ad1843_DRSFLT = { 25, 15,  1 },	/* Digital Reampler Filter Mode */
+	ad1843_ADLF   = { 26,  0,  2 }, /* ADC Left Channel Data Format */
+	ad1843_ADRF   = { 26,  2,  2 }, /* ADC Right Channel Data Format */
+	ad1843_ADTLK  = { 26,  4,  1 },	/* ADC Transmit Lock Mode Select */
+	ad1843_SCF    = { 26,  7,  1 },	/* SCLK Frequency Select */
+	ad1843_DA1F   = { 26,  8,  2 },	/* DAC1 Data Format Select */
+	ad1843_DA2F   = { 26, 10,  2 },	/* DAC2 Data Format Select */
+	ad1843_DA1SM  = { 26, 14,  1 },	/* DAC1 Stereo/Mono Mode Select */
+	ad1843_DA2SM  = { 26, 15,  1 },	/* DAC2 Stereo/Mono Mode Select */
+	ad1843_ADLEN  = { 27,  0,  1 },	/* ADC Left Channel Enable */
+	ad1843_ADREN  = { 27,  1,  1 },	/* ADC Right Channel Enable */
+	ad1843_AAMEN  = { 27,  4,  1 },	/* Analog to Analog Mix Enable */
+	ad1843_ANAEN  = { 27,  7,  1 },	/* Analog Channel Enable */
+	ad1843_DA1EN  = { 27,  8,  1 },	/* DAC1 Enable */
+	ad1843_DA2EN  = { 27,  9,  1 },	/* DAC2 Enable */
+	ad1843_DDMEN  = { 27, 12,  1 },	/* DAC2 to DAC1 Mix  Enable */
+	ad1843_C1EN   = { 28, 11,  1 },	/* Clock Generator 1 Enable */
+	ad1843_C2EN   = { 28, 12,  1 },	/* Clock Generator 2 Enable */
+	ad1843_C3EN   = { 28, 13,  1 },	/* Clock Generator 3 Enable */
+	ad1843_PDNI   = { 28, 15,  1 };	/* Converter Power Down */
+
+/*
+ * The various registers of the AD1843 use three different formats for
+ * specifying gain.  The ad1843_gain structure parameterizes the
+ * formats.
+ */
+
+struct ad1843_gain {
+	int	negative;		/* nonzero if gain is negative. */
+	const struct ad1843_bitfield *lfield;
+	const struct ad1843_bitfield *rfield;
+	const struct ad1843_bitfield *lmute;
+	const struct ad1843_bitfield *rmute;
+};
+
+static const struct ad1843_gain ad1843_gain_RECLEV = {
+	.negative = 0,
+	.lfield   = &ad1843_LIG,
+	.rfield   = &ad1843_RIG
+};
+static const struct ad1843_gain ad1843_gain_LINE = {
+	.negative = 1,
+	.lfield   = &ad1843_LX1M,
+	.rfield   = &ad1843_RX1M,
+	.lmute    = &ad1843_LX1MM,
+	.rmute    = &ad1843_RX1MM
+};
+static const struct ad1843_gain ad1843_gain_LINE_2 = {
+	.negative = 1,
+	.lfield   = &ad1843_LDA2G,
+	.rfield   = &ad1843_RDA2G,
+	.lmute    = &ad1843_LDA2GM,
+	.rmute    = &ad1843_RDA2GM
+};
+static const struct ad1843_gain ad1843_gain_MIC = {
+	.negative = 1,
+	.lfield   = &ad1843_LMCM,
+	.rfield   = &ad1843_RMCM,
+	.lmute    = &ad1843_LMCMM,
+	.rmute    = &ad1843_RMCMM
+};
+static const struct ad1843_gain ad1843_gain_PCM_0 = {
+	.negative = 1,
+	.lfield   = &ad1843_LDA1G,
+	.rfield   = &ad1843_RDA1G,
+	.lmute    = &ad1843_LDA1GM,
+	.rmute    = &ad1843_RDA1GM
+};
+static const struct ad1843_gain ad1843_gain_PCM_1 = {
+	.negative = 1,
+	.lfield   = &ad1843_LD2M,
+	.rfield   = &ad1843_RD2M,
+	.lmute    = &ad1843_LD2MM,
+	.rmute    = &ad1843_RD2MM
+};
+
+static const struct ad1843_gain *ad1843_gain[AD1843_GAIN_SIZE] =
+{
+	&ad1843_gain_RECLEV,
+	&ad1843_gain_LINE,
+	&ad1843_gain_LINE_2,
+	&ad1843_gain_MIC,
+	&ad1843_gain_PCM_0,
+	&ad1843_gain_PCM_1,
+};
+
+/* read the current value of an AD1843 bitfield. */
+
+static int ad1843_read_bits(struct snd_ad1843 *ad1843,
+			    const struct ad1843_bitfield *field)
+{
+	int w;
+
+	w = ad1843->read(ad1843->chip, field->reg);
+	return w >> field->lo_bit & ((1 << field->nbits) - 1);
+}
+
+/*
+ * write a new value to an AD1843 bitfield and return the old value.
+ */
+
+static int ad1843_write_bits(struct snd_ad1843 *ad1843,
+			     const struct ad1843_bitfield *field,
+			     int newval)
+{
+	int w, mask, oldval, newbits;
+
+	w = ad1843->read(ad1843->chip, field->reg);
+	mask = ((1 << field->nbits) - 1) << field->lo_bit;
+	oldval = (w & mask) >> field->lo_bit;
+	newbits = (newval << field->lo_bit) & mask;
+	w = (w & ~mask) | newbits;
+	ad1843->write(ad1843->chip, field->reg, w);
+
+	return oldval;
+}
+
+/*
+ * ad1843_read_multi reads multiple bitfields from the same AD1843
+ * register.  It uses a single read cycle to do it.  (Reading the
+ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
+ * microseconds.)
+ *
+ * Called like this.
+ *
+ *  ad1843_read_multi(ad1843, nfields,
+ *		      &ad1843_FIELD1, &val1,
+ *		      &ad1843_FIELD2, &val2, ...);
+ */
+
+static void ad1843_read_multi(struct snd_ad1843 *ad1843, int argcount, ...)
+{
+	va_list ap;
+	const struct ad1843_bitfield *fp;
+	int w = 0, mask, *value, reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const struct ad1843_bitfield *);
+		value = va_arg(ap, int *);
+		if (reg == -1) {
+			reg = fp->reg;
+			w = ad1843->read(ad1843->chip, reg);
+		}
+
+		mask = (1 << fp->nbits) - 1;
+		*value = w >> fp->lo_bit & mask;
+	}
+	va_end(ap);
+}
+
+/*
+ * ad1843_write_multi stores multiple bitfields into the same AD1843
+ * register.  It uses one read and one write cycle to do it.
+ *
+ * Called like this.
+ *
+ *  ad1843_write_multi(ad1843, nfields,
+ *		       &ad1843_FIELD1, val1,
+ *		       &ad1843_FIELF2, val2, ...);
+ */
+
+static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
+{
+	va_list ap;
+	int reg;
+	const struct ad1843_bitfield *fp;
+	int value;
+	int w, m, mask, bits;
+
+	mask = 0;
+	bits = 0;
+	reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const struct ad1843_bitfield *);
+		value = va_arg(ap, int);
+		if (reg == -1)
+			reg = fp->reg;
+		else
+			BUG_ON(reg != fp->reg);
+		m = ((1 << fp->nbits) - 1) << fp->lo_bit;
+		mask |= m;
+		bits |= (value << fp->lo_bit) & m;
+	}
+	va_end(ap);
+
+	if (~mask & 0xFFFF)
+		w = ad1843->read(ad1843->chip, reg);
+	else
+		w = 0;
+	w = (w & ~mask) | bits;
+	ad1843->write(ad1843->chip, reg, w);
+}
+
+int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id)
+{
+	const struct ad1843_gain *gp = ad1843_gain[id];
+	int ret;
+
+	ret = (1 << gp->lfield->nbits);
+	if (!gp->lmute)
+		ret -= 1;
+	return ret;
+}
+
+/*
+ * ad1843_get_gain reads the specified register and extracts the gain value
+ * using the supplied gain type.
+ */
+
+int ad1843_get_gain(struct snd_ad1843 *ad1843, int id)
+{
+	int lg, rg, lm, rm;
+	const struct ad1843_gain *gp = ad1843_gain[id];
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	ad1843_read_multi(ad1843, 2, gp->lfield, &lg, gp->rfield, &rg);
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	if (gp->lmute) {
+		ad1843_read_multi(ad1843, 2, gp->lmute, &lm, gp->rmute, &rm);
+		if (lm)
+			lg = 0;
+		if (rm)
+			rg = 0;
+	}
+	return lg << 0 | rg << 8;
+}
+
+/*
+ * Set an audio channel's gain.
+ *
+ * Returns the new gain, which may be lower than the old gain.
+ */
+
+int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval)
+{
+	const struct ad1843_gain *gp = ad1843_gain[id];
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	int lg = (newval >> 0) & mask;
+	int rg = (newval >> 8) & mask;
+	int lm = (lg == 0) ? 1 : 0;
+	int rm = (rg == 0) ? 1 : 0;
+
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	if (gp->lmute)
+		ad1843_write_multi(ad1843, 2, gp->lmute, lm, gp->rmute, rm);
+	ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg);
+	return ad1843_get_gain(ad1843, id);
+}
+
+/* Returns the current recording source */
+
+int ad1843_get_recsrc(struct snd_ad1843 *ad1843)
+{
+	int val = ad1843_read_bits(ad1843, &ad1843_LSS);
+
+	if (val < 0 || val > 2) {
+		val = 2;
+		ad1843_write_multi(ad1843, 2,
+				   &ad1843_LSS, val, &ad1843_RSS, val);
+	}
+	return val;
+}
+
+/*
+ * Set recording source.
+ *
+ * Returns newsrc on success, -errno on failure.
+ */
+
+int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc)
+{
+	if (newsrc < 0 || newsrc > 2)
+		return -EINVAL;
+
+	ad1843_write_multi(ad1843, 2, &ad1843_LSS, newsrc, &ad1843_RSS, newsrc);
+	return newsrc;
+}
+
+/* Setup ad1843 for D/A conversion. */
+
+void ad1843_setup_dac(struct snd_ad1843 *ad1843,
+		      unsigned int id,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels)
+{
+	int ad_fmt = 0, ad_mode = 0;
+
+	switch (fmt) {
+	case SNDRV_PCM_FORMAT_S8:
+		ad_fmt = 0;
+		break;
+	case SNDRV_PCM_FORMAT_U8:
+		ad_fmt = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ad_fmt = 1;
+		break;
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		ad_fmt = 2;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		ad_fmt = 3;
+		break;
+	default:
+		break;
+	}
+
+	switch (channels) {
+	case 2:
+		ad_mode = 0;
+		break;
+	case 1:
+		ad_mode = 1;
+		break;
+	default:
+		break;
+	}
+
+	if (id) {
+		ad1843_write_bits(ad1843, &ad1843_C2C, framerate);
+		ad1843_write_multi(ad1843, 2,
+				   &ad1843_DA2SM, ad_mode,
+				   &ad1843_DA2F, ad_fmt);
+	} else {
+		ad1843_write_bits(ad1843, &ad1843_C1C, framerate);
+		ad1843_write_multi(ad1843, 2,
+				   &ad1843_DA1SM, ad_mode,
+				   &ad1843_DA1F, ad_fmt);
+	}
+}
+
+void ad1843_shutdown_dac(struct snd_ad1843 *ad1843, unsigned int id)
+{
+	if (id)
+		ad1843_write_bits(ad1843, &ad1843_DA2F, 1);
+	else
+		ad1843_write_bits(ad1843, &ad1843_DA1F, 1);
+}
+
+void ad1843_setup_adc(struct snd_ad1843 *ad1843,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels)
+{
+	int da_fmt = 0;
+
+	switch (fmt) {
+	case SNDRV_PCM_FORMAT_S8:	da_fmt = 0; break;
+	case SNDRV_PCM_FORMAT_U8:	da_fmt = 0; break;
+	case SNDRV_PCM_FORMAT_S16_LE:	da_fmt = 1; break;
+	case SNDRV_PCM_FORMAT_MU_LAW:	da_fmt = 2; break;
+	case SNDRV_PCM_FORMAT_A_LAW:	da_fmt = 3; break;
+	default:		break;
+	}
+
+	ad1843_write_bits(ad1843, &ad1843_C3C, framerate);
+	ad1843_write_multi(ad1843, 2,
+			   &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
+}
+
+void ad1843_shutdown_adc(struct snd_ad1843 *ad1843)
+{
+	/* nothing to do */
+}
+
+/*
+ * Fully initialize the ad1843.  As described in the AD1843 data
+ * sheet, section "START-UP SEQUENCE".  The numbered comments are
+ * subsection headings from the data sheet.  See the data sheet, pages
+ * 52-54, for more info.
+ *
+ * return 0 on success, -errno on failure.  */
+
+int ad1843_init(struct snd_ad1843 *ad1843)
+{
+	unsigned long later;
+
+	if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) {
+		printk(KERN_ERR "ad1843: AD1843 won't initialize\n");
+		return -EIO;
+	}
+
+	ad1843_write_bits(ad1843, &ad1843_SCF, 1);
+
+	/* 4. Put the conversion resources into standby. */
+	ad1843_write_bits(ad1843, &ad1843_PDNI, 0);
+	later = jiffies + msecs_to_jiffies(500);
+
+	while (ad1843_read_bits(ad1843, &ad1843_PDNO)) {
+		if (time_after(jiffies, later)) {
+			printk(KERN_ERR
+			       "ad1843: AD1843 won't power up\n");
+			return -EIO;
+		}
+		schedule_timeout_interruptible(5);
+	}
+
+	/* 5. Power up the clock generators and enable clock output pins. */
+	ad1843_write_multi(ad1843, 3,
+			   &ad1843_C1EN, 1,
+			   &ad1843_C2EN, 1,
+			   &ad1843_C3EN, 1);
+
+	/* 6. Configure conversion resources while they are in standby. */
+
+	/* DAC1/2 use clock 1/2 as source, ADC uses clock 3.  Always. */
+	ad1843_write_multi(ad1843, 4,
+			   &ad1843_DA1C, 1,
+			   &ad1843_DA2C, 2,
+			   &ad1843_ADLC, 3,
+			   &ad1843_ADRC, 3);
+
+	/* 7. Enable conversion resources. */
+	ad1843_write_bits(ad1843, &ad1843_ADTLK, 1);
+	ad1843_write_multi(ad1843, 7,
+			   &ad1843_ANAEN, 1,
+			   &ad1843_AAMEN, 1,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_DA2EN, 1,
+			   &ad1843_DDMEN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* 8. Configure conversion resources while they are enabled. */
+
+	/* set gain to 0 for all channels */
+	ad1843_set_gain(ad1843, AD1843_GAIN_RECLEV, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_LINE, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_LINE_2, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_MIC, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_PCM_0, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_PCM_1, 0);
+
+	/* Unmute all channels. */
+	/* DAC1 */
+	ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0);
+	/* DAC2 */
+	ad1843_write_multi(ad1843, 2, &ad1843_LDA2GM, 0, &ad1843_RDA2GM, 0);
+
+	/* Set default recording source to Line In and set
+	 * mic gain to +20 dB.
+	 */
+	ad1843_set_recsrc(ad1843, 2);
+	ad1843_write_multi(ad1843, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
+
+	/* Set Speaker Out level to +/- 4V and unmute it. */
+	ad1843_write_multi(ad1843, 3,
+			   &ad1843_HPOS, 1,
+			   &ad1843_HPOM, 0,
+			   &ad1843_MPOM, 0);
+
+	return 0;
+}
diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c
new file mode 100644
index 0000000000000000000000000000000000000000..db495be01861ffee559d34221ff82a8624214502
--- /dev/null
+++ b/sound/mips/hal2.c
@@ -0,0 +1,947 @@
+/*
+ *  Driver for A2 audio system used in SGI machines
+ *  Copyright (c) 2008 Thomas Bogendoerfer <tsbogend@alpha.fanken.de>
+ *
+ *  Based on OSS code from Ladislav Michl <ladis@linux-mips.org>, which
+ *  was based on code from Ulf Carlsson
+ *
+ *  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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm-indirect.h>
+#include <sound/initval.h>
+
+#include "hal2.h"
+
+static int index = SNDRV_DEFAULT_IDX1;  /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;   /* ID for this card */
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SGI HAL2 soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SGI HAL2 soundcard.");
+MODULE_DESCRIPTION("ALSA driver for SGI HAL2 audio");
+MODULE_AUTHOR("Thomas Bogendoerfer");
+MODULE_LICENSE("GPL");
+
+
+#define H2_BLOCK_SIZE	1024
+#define H2_BUF_SIZE	16384
+
+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 pad;			/* padding */
+};
+
+struct hal2_codec {
+	struct snd_pcm_indirect pcm_indirect;
+	struct snd_pcm_substream *substream;
+
+	unsigned char *buffer;
+	dma_addr_t buffer_dma;
+	struct hal2_desc *desc;
+	dma_addr_t desc_dma;
+	int desc_count;
+	struct hal2_pbus pbus;
+	int voices;			/* mono/stereo */
+	unsigned int sample_rate;
+	unsigned int master;		/* Master frequency */
+	unsigned short mod;		/* MOD value */
+	unsigned short inc;		/* INC value */
+};
+
+#define H2_MIX_OUTPUT_ATT	0
+#define H2_MIX_INPUT_GAIN	1
+
+struct snd_hal2 {
+	struct snd_card *card;
+
+	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;
+};
+
+#define H2_INDIRECT_WAIT(regs)	while (hal2_read(&regs->isr) & H2_ISR_TSTATUS);
+
+#define H2_READ_ADDR(addr)	(addr | (1<<7))
+#define H2_WRITE_ADDR(addr)	(addr)
+
+static inline u32 hal2_read(u32 *reg)
+{
+	return __raw_readl(reg);
+}
+
+static inline void hal2_write(u32 val, u32 *reg)
+{
+	__raw_writel(val, reg);
+}
+
+
+static u32 hal2_i_read32(struct snd_hal2 *hal2, u16 addr)
+{
+	u32 ret;
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(H2_READ_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	ret = hal2_read(&regs->idr0) & 0xffff;
+	hal2_write(H2_READ_ADDR(addr) | 0x1, &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	ret |= (hal2_read(&regs->idr0) & 0xffff) << 16;
+	return ret;
+}
+
+static void hal2_i_write16(struct snd_hal2 *hal2, u16 addr, u16 val)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(val, &regs->idr0);
+	hal2_write(0, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_write32(struct snd_hal2 *hal2, u16 addr, u32 val)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(val & 0xffff, &regs->idr0);
+	hal2_write(val >> 16, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_setbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(H2_READ_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	hal2_write((hal2_read(&regs->idr0) & 0xffff) | bit, &regs->idr0);
+	hal2_write(0, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_clearbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(H2_READ_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	hal2_write((hal2_read(&regs->idr0) & 0xffff) & ~bit, &regs->idr0);
+	hal2_write(0, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static int hal2_gain_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	switch ((int)kcontrol->private_value) {
+	case H2_MIX_OUTPUT_ATT:
+		uinfo->value.integer.max = 31;
+		break;
+	case H2_MIX_INPUT_GAIN:
+		uinfo->value.integer.max = 15;
+		break;
+	}
+	return 0;
+}
+
+static int hal2_gain_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
+	u32 tmp;
+	int l, r;
+
+	switch ((int)kcontrol->private_value) {
+	case H2_MIX_OUTPUT_ATT:
+		tmp = hal2_i_read32(hal2, H2I_DAC_C2);
+		if (tmp & H2I_C2_MUTE) {
+			l = 0;
+			r = 0;
+		} else {
+			l = 31 - ((tmp >> H2I_C2_L_ATT_SHIFT) & 31);
+			r = 31 - ((tmp >> H2I_C2_R_ATT_SHIFT) & 31);
+		}
+		break;
+	case H2_MIX_INPUT_GAIN:
+		tmp = hal2_i_read32(hal2, H2I_ADC_C2);
+		l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15;
+		r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15;
+		break;
+	}
+	ucontrol->value.integer.value[0] = l;
+	ucontrol->value.integer.value[1] = r;
+
+	return 0;
+}
+
+static int hal2_gain_put(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
+	u32 old, new;
+	int l, r;
+
+	l = ucontrol->value.integer.value[0];
+	r = ucontrol->value.integer.value[1];
+
+	switch ((int)kcontrol->private_value) {
+	case H2_MIX_OUTPUT_ATT:
+		old = hal2_i_read32(hal2, H2I_DAC_C2);
+		new = old & ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
+		if (l | r) {
+			l = 31 - l;
+			r = 31 - r;
+			new |= (l << H2I_C2_L_ATT_SHIFT);
+			new |= (r << H2I_C2_R_ATT_SHIFT);
+		} else
+			new |= H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE;
+		hal2_i_write32(hal2, H2I_DAC_C2, new);
+		break;
+	case H2_MIX_INPUT_GAIN:
+		old = hal2_i_read32(hal2, H2I_ADC_C2);
+		new = old & ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M);
+		new |= (l << H2I_C2_L_GAIN_SHIFT);
+		new |= (r << H2I_C2_R_GAIN_SHIFT);
+		hal2_i_write32(hal2, H2I_ADC_C2, new);
+		break;
+	}
+	return old != new;
+}
+
+static struct snd_kcontrol_new hal2_ctrl_headphone __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Headphone Playback Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = H2_MIX_OUTPUT_ATT,
+	.info           = hal2_gain_info,
+	.get            = hal2_gain_get,
+	.put            = hal2_gain_put,
+};
+
+static struct snd_kcontrol_new hal2_ctrl_mic __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Mic Capture Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = H2_MIX_INPUT_GAIN,
+	.info           = hal2_gain_info,
+	.get            = hal2_gain_get,
+	.put            = hal2_gain_put,
+};
+
+static int __devinit hal2_mixer_create(struct snd_hal2 *hal2)
+{
+	int err;
+
+	/* mute DAC */
+	hal2_i_write32(hal2, H2I_DAC_C2,
+		       H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
+	/* mute ADC */
+	hal2_i_write32(hal2, H2I_ADC_C2, 0);
+
+	err = snd_ctl_add(hal2->card,
+			  snd_ctl_new1(&hal2_ctrl_headphone, hal2));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(hal2->card,
+			  snd_ctl_new1(&hal2_ctrl_mic, hal2));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static irqreturn_t hal2_interrupt(int irq, void *dev_id)
+{
+	struct snd_hal2 *hal2 = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+
+	/* decide what caused this interrupt */
+	if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
+		snd_pcm_period_elapsed(hal2->dac.substream);
+		ret = IRQ_HANDLED;
+	}
+	if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
+		snd_pcm_period_elapsed(hal2->adc.substream);
+		ret = IRQ_HANDLED;
+	}
+	return ret;
+}
+
+static int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate)
+{
+	unsigned short mod;
+
+	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;
+
+	return rate;
+}
+
+static void hal2_set_dac_rate(struct snd_hal2 *hal2)
+{
+	unsigned int master = hal2->dac.master;
+	int inc = hal2->dac.inc;
+	int mod = hal2->dac.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 snd_hal2 *hal2)
+{
+	unsigned int master = hal2->adc.master;
+	int inc = hal2->adc.inc;
+	int mod = hal2->adc.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 snd_hal2 *hal2)
+{
+	unsigned int fifobeg, fifoend, highwater, sample_size;
+	struct hal2_pbus *pbus = &hal2->dac.pbus;
+
+	/* 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);
+	/* 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 */
+	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 snd_hal2 *hal2)
+{
+	unsigned int fifobeg, fifoend, highwater, sample_size;
+	struct hal2_pbus *pbus = &hal2->adc.pbus;
+
+	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);
+	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 */
+	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 void hal2_start_dac(struct snd_hal2 *hal2)
+{
+	struct hal2_pbus *pbus = &hal2->dac.pbus;
+
+	pbus->pbus->pbdma_dptr = hal2->dac.desc_dma;
+	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 snd_hal2 *hal2)
+{
+	struct hal2_pbus *pbus = &hal2->adc.pbus;
+
+	pbus->pbus->pbdma_dptr = hal2->adc.desc_dma;
+	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 snd_hal2 *hal2)
+{
+	hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+	/* The HAL2 itself may remain enabled safely */
+}
+
+static inline void hal2_stop_adc(struct snd_hal2 *hal2)
+{
+	hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+}
+
+static int hal2_alloc_dmabuf(struct hal2_codec *codec)
+{
+	struct hal2_desc *desc;
+	dma_addr_t desc_dma, buffer_dma;
+	int count = H2_BUF_SIZE / H2_BLOCK_SIZE;
+	int i;
+
+	codec->buffer = dma_alloc_noncoherent(NULL, H2_BUF_SIZE,
+					      &buffer_dma, GFP_KERNEL);
+	if (!codec->buffer)
+		return -ENOMEM;
+	desc = dma_alloc_noncoherent(NULL, count * sizeof(struct hal2_desc),
+				     &desc_dma, GFP_KERNEL);
+	if (!desc) {
+		dma_free_noncoherent(NULL, H2_BUF_SIZE,
+				     codec->buffer, buffer_dma);
+		return -ENOMEM;
+	}
+	codec->buffer_dma = buffer_dma;
+	codec->desc_dma = desc_dma;
+	codec->desc = desc;
+	for (i = 0; i < count; i++) {
+		desc->desc.pbuf = buffer_dma + i * H2_BLOCK_SIZE;
+		desc->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE;
+		desc->desc.pnext = (i == count - 1) ?
+		      desc_dma : desc_dma + (i + 1) * sizeof(struct hal2_desc);
+		desc++;
+	}
+	dma_cache_sync(NULL, codec->desc, count * sizeof(struct hal2_desc),
+		       DMA_TO_DEVICE);
+	codec->desc_count = count;
+	return 0;
+}
+
+static void hal2_free_dmabuf(struct hal2_codec *codec)
+{
+	dma_free_noncoherent(NULL, codec->desc_count * sizeof(struct hal2_desc),
+			     codec->desc, codec->desc_dma);
+	dma_free_noncoherent(NULL, H2_BUF_SIZE, codec->buffer,
+			     codec->buffer_dma);
+}
+
+static struct snd_pcm_hardware hal2_pcm_hw = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
+	.rates =            SNDRV_PCM_RATE_8000_48000,
+	.rate_min =         8000,
+	.rate_max =         48000,
+	.channels_min =     2,
+	.channels_max =     2,
+	.buffer_bytes_max = 65536,
+	.period_bytes_min = 1024,
+	.period_bytes_max = 65536,
+	.periods_min =      2,
+	.periods_max =      1024,
+};
+
+static int hal2_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	int err;
+
+	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int hal2_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int hal2_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	int err;
+
+	runtime->hw = hal2_pcm_hw;
+
+	err = hal2_alloc_dmabuf(&hal2->dac);
+	if (err)
+		return err;
+	return 0;
+}
+
+static int hal2_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	hal2_free_dmabuf(&hal2->dac);
+	return 0;
+}
+
+static int hal2_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hal2_codec *dac = &hal2->dac;
+
+	dac->voices = runtime->channels;
+	dac->sample_rate = hal2_compute_rate(dac, runtime->rate);
+	memset(&dac->pcm_indirect, 0, sizeof(dac->pcm_indirect));
+	dac->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
+	dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	dac->substream = substream;
+	hal2_setup_dac(hal2);
+	return 0;
+}
+
+static int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		hal2->dac.pcm_indirect.hw_io = hal2->dac.buffer_dma;
+		hal2->dac.pcm_indirect.hw_data = 0;
+		substream->ops->ack(substream);
+		hal2_start_dac(hal2);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		hal2_stop_dac(hal2);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t
+hal2_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *dac = &hal2->dac;
+
+	return snd_pcm_indirect_playback_pointer(substream, &dac->pcm_indirect,
+						 dac->pbus.pbus->pbdma_bptr);
+}
+
+static void hal2_playback_transfer(struct snd_pcm_substream *substream,
+				   struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	unsigned char *buf = hal2->dac.buffer + rec->hw_data;
+
+	memcpy(buf, substream->runtime->dma_area + rec->sw_data, bytes);
+	dma_cache_sync(NULL, buf, bytes, DMA_TO_DEVICE);
+
+}
+
+static int hal2_playback_ack(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *dac = &hal2->dac;
+
+	dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+	snd_pcm_indirect_playback_transfer(substream,
+					   &dac->pcm_indirect,
+					   hal2_playback_transfer);
+	return 0;
+}
+
+static int hal2_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *adc = &hal2->adc;
+	int err;
+
+	runtime->hw = hal2_pcm_hw;
+
+	err = hal2_alloc_dmabuf(adc);
+	if (err)
+		return err;
+	return 0;
+}
+
+static int hal2_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	hal2_free_dmabuf(&hal2->adc);
+	return 0;
+}
+
+static int hal2_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hal2_codec *adc = &hal2->adc;
+
+	adc->voices = runtime->channels;
+	adc->sample_rate = hal2_compute_rate(adc, runtime->rate);
+	memset(&adc->pcm_indirect, 0, sizeof(adc->pcm_indirect));
+	adc->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
+	adc->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+	adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	adc->substream = substream;
+	hal2_setup_adc(hal2);
+	return 0;
+}
+
+static int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		hal2->adc.pcm_indirect.hw_io = hal2->adc.buffer_dma;
+		hal2->adc.pcm_indirect.hw_data = 0;
+		printk(KERN_DEBUG "buffer_dma %x\n", hal2->adc.buffer_dma);
+		hal2_start_adc(hal2);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		hal2_stop_adc(hal2);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t
+hal2_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *adc = &hal2->adc;
+
+	return snd_pcm_indirect_capture_pointer(substream, &adc->pcm_indirect,
+						adc->pbus.pbus->pbdma_bptr);
+}
+
+static void hal2_capture_transfer(struct snd_pcm_substream *substream,
+				  struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	unsigned char *buf = hal2->adc.buffer + rec->hw_data;
+
+	dma_cache_sync(NULL, buf, bytes, DMA_FROM_DEVICE);
+	memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes);
+}
+
+static int hal2_capture_ack(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *adc = &hal2->adc;
+
+	snd_pcm_indirect_capture_transfer(substream,
+					  &adc->pcm_indirect,
+					  hal2_capture_transfer);
+	return 0;
+}
+
+static struct snd_pcm_ops hal2_playback_ops = {
+	.open =        hal2_playback_open,
+	.close =       hal2_playback_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   hal2_pcm_hw_params,
+	.hw_free =     hal2_pcm_hw_free,
+	.prepare =     hal2_playback_prepare,
+	.trigger =     hal2_playback_trigger,
+	.pointer =     hal2_playback_pointer,
+	.ack =         hal2_playback_ack,
+};
+
+static struct snd_pcm_ops hal2_capture_ops = {
+	.open =        hal2_capture_open,
+	.close =       hal2_capture_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   hal2_pcm_hw_params,
+	.hw_free =     hal2_pcm_hw_free,
+	.prepare =     hal2_capture_prepare,
+	.trigger =     hal2_capture_trigger,
+	.pointer =     hal2_capture_pointer,
+	.ack =         hal2_capture_ack,
+};
+
+static int __devinit hal2_pcm_create(struct snd_hal2 *hal2)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	/* create first pcm device with one outputs and one input */
+	err = snd_pcm_new(hal2->card, "SGI HAL2 Audio", 0, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = hal2;
+	strcpy(pcm->name, "SGI HAL2");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&hal2_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&hal2_capture_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					   snd_dma_continuous_data(GFP_KERNEL),
+					   0, 1024 * 1024);
+
+	return 0;
+}
+
+static int hal2_dev_free(struct snd_device *device)
+{
+	struct snd_hal2 *hal2 = device->device_data;
+
+	free_irq(SGI_HPCDMA_IRQ, hal2);
+	kfree(hal2);
+	return 0;
+}
+
+static struct snd_device_ops hal2_ops = {
+	.dev_free = hal2_dev_free,
+};
+
+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];
+}
+
+static int hal2_detect(struct snd_hal2 *hal2)
+{
+	unsigned short board, major, minor;
+	unsigned short rev;
+
+	/* reset HAL2 */
+	hal2_write(0, &hal2->ctl_regs->isr);
+
+	/* release reset */
+	hal2_write(H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N,
+		   &hal2->ctl_regs->isr);
+
+
+	hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE);
+	rev = hal2_read(&hal2->ctl_regs->rev);
+	if (rev & 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_create(struct snd_card *card, struct snd_hal2 **rchip)
+{
+	struct snd_hal2 *hal2;
+	struct hpc3_regs *hpc3 = hpc3c0;
+	int err;
+
+	hal2 = kzalloc(sizeof(struct snd_hal2), GFP_KERNEL);
+	if (!hal2)
+		return -ENOMEM;
+
+	hal2->card = card;
+
+	if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
+			"SGI HAL2", hal2)) {
+		printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
+		kfree(hal2);
+		return -EAGAIN;
+	}
+
+	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) {
+		kfree(hal2);
+		return -ENODEV;
+	}
+
+	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;
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hal2, &hal2_ops);
+	if (err < 0) {
+		free_irq(SGI_HPCDMA_IRQ, hal2);
+		kfree(hal2);
+		return err;
+	}
+	*rchip = hal2;
+	return 0;
+}
+
+static int __devinit hal2_probe(struct platform_device *pdev)
+{
+	struct snd_card *card;
+	struct snd_hal2 *chip;
+	int err;
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	err = hal2_create(card, &chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_card_set_dev(card, &pdev->dev);
+
+	err = hal2_pcm_create(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	err = hal2_mixer_create(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "SGI HAL2 Audio");
+	strcpy(card->shortname, "SGI HAL2 Audio");
+	sprintf(card->longname, "%s irq %i",
+		card->shortname,
+		SGI_HPCDMA_IRQ);
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	platform_set_drvdata(pdev, card);
+	return 0;
+}
+
+static int __exit hal2_remove(struct platform_device *pdev)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+
+	snd_card_free(card);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver hal2_driver = {
+	.probe	= hal2_probe,
+	.remove	= __devexit_p(hal2_remove),
+	.driver = {
+		.name	= "sgihal2",
+		.owner	= THIS_MODULE,
+	}
+};
+
+static int __init alsa_card_hal2_init(void)
+{
+	return platform_driver_register(&hal2_driver);
+}
+
+static void __exit alsa_card_hal2_exit(void)
+{
+	platform_driver_unregister(&hal2_driver);
+}
+
+module_init(alsa_card_hal2_init);
+module_exit(alsa_card_hal2_exit);
diff --git a/sound/mips/hal2.h b/sound/mips/hal2.h
new file mode 100644
index 0000000000000000000000000000000000000000..f19828bc64e00b08876f2b7e733c493cccc65b25
--- /dev/null
+++ b/sound/mips/hal2.h
@@ -0,0 +1,245 @@
+#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 <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];
+	u32 isr;		/* 0x10 Status Register */
+	u32 _unused1[3];
+	u32 rev;		/* 0x20 Revision Register */
+	u32 _unused2[3];
+	u32 iar;		/* 0x30 Indirect Address Register */
+	u32 _unused3[3];
+	u32 idr0;		/* 0x40 Indirect Data Register 0 */
+	u32 _unused4[3];
+	u32 idr1;		/* 0x50 Indirect Data Register 1 */
+	u32 _unused5[3];
+	u32 idr2;		/* 0x60 Indirect Data Register 2 */
+	u32 _unused6[3];
+	u32 idr3;		/* 0x70 Indirect Data Register 3 */
+};
+
+struct hal2_aes_regs {
+	u32 rx_stat[2];	/* Status registers */
+	u32 rx_cr[2];		/* Control registers */
+	u32 rx_ud[4];		/* User data window */
+	u32 rx_st[24];		/* Channel status data */
+
+	u32 tx_stat[1];	/* Status register */
+	u32 tx_cr[3];		/* Control registers */
+	u32 tx_ud[4];		/* User data window */
+	u32 tx_st[24];		/* Channel status data */
+};
+
+struct hal2_vol_regs {
+	u32 right;		/* Right volume */
+	u32 left;		/* Left volume */
+};
+
+struct hal2_syn_regs {
+	u32 _unused0[2];
+	u32 page;		/* DOC Page register */
+	u32 regsel;		/* DOC Register selection */
+	u32 dlow;		/* DOC Data low */
+	u32 dhigh;		/* DOC Data high */
+	u32 irq;		/* IRQ Status */
+	u32 dram;		/* DRAM Access */
+};
+
+#endif	/* __HAL2_H */
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
new file mode 100644
index 0000000000000000000000000000000000000000..4c63504348dc4ba155cfd8ab79a4e03347db8d84
--- /dev/null
+++ b/sound/mips/sgio2audio.c
@@ -0,0 +1,1006 @@
+/*
+ *   Sound driver for Silicon Graphics O2 Workstations A/V board audio.
+ *
+ *   Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ *   Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ *   Mxier part taken from mace_audio.c:
+ *   Copyright 2007 Thorben Jändling <tj.trevelyan@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.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/gfp.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/ip32/ip32_ints.h>
+#include <asm/ip32/mace.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/ad1843.h>
+
+
+MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org>");
+MODULE_DESCRIPTION("SGI O2 Audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}");
+
+static int index = SNDRV_DEFAULT_IDX1;  /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;   /* ID for this card */
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SGI O2 soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SGI O2 soundcard.");
+
+
+#define AUDIO_CONTROL_RESET              BIT(0) /* 1: reset audio interface */
+#define AUDIO_CONTROL_CODEC_PRESENT      BIT(1) /* 1: codec detected */
+
+#define CODEC_CONTROL_WORD_SHIFT        0
+#define CODEC_CONTROL_READ              BIT(16)
+#define CODEC_CONTROL_ADDRESS_SHIFT     17
+
+#define CHANNEL_CONTROL_RESET           BIT(10) /* 1: reset channel */
+#define CHANNEL_DMA_ENABLE              BIT(9)  /* 1: enable DMA transfer */
+#define CHANNEL_INT_THRESHOLD_DISABLED  (0 << 5) /* interrupt disabled */
+#define CHANNEL_INT_THRESHOLD_25        (1 << 5) /* int on buffer >25% full */
+#define CHANNEL_INT_THRESHOLD_50        (2 << 5) /* int on buffer >50% full */
+#define CHANNEL_INT_THRESHOLD_75        (3 << 5) /* int on buffer >75% full */
+#define CHANNEL_INT_THRESHOLD_EMPTY     (4 << 5) /* int on buffer empty */
+#define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
+#define CHANNEL_INT_THRESHOLD_FULL      (6 << 5) /* int on buffer empty */
+#define CHANNEL_INT_THRESHOLD_NOT_FULL  (7 << 5) /* int on buffer !empty */
+
+#define CHANNEL_RING_SHIFT              12
+#define CHANNEL_RING_SIZE               (1 << CHANNEL_RING_SHIFT)
+#define CHANNEL_RING_MASK               (CHANNEL_RING_SIZE - 1)
+
+#define CHANNEL_LEFT_SHIFT 40
+#define CHANNEL_RIGHT_SHIFT 8
+
+struct snd_sgio2audio_chan {
+	int idx;
+	struct snd_pcm_substream *substream;
+	int pos;
+	snd_pcm_uframes_t size;
+	spinlock_t lock;
+};
+
+/* definition of the chip-specific record */
+struct snd_sgio2audio {
+	struct snd_card *card;
+
+	/* codec */
+	struct snd_ad1843 ad1843;
+	spinlock_t ad1843_lock;
+
+	/* channels */
+	struct snd_sgio2audio_chan channel[3];
+
+	/* resources */
+	void *ring_base;
+	dma_addr_t ring_base_dma;
+};
+
+/* AD1843 access */
+
+/*
+ * read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
+ *
+ * Returns unsigned register value on success, -errno on failure.
+ */
+static int read_ad1843_reg(void *priv, int reg)
+{
+	struct snd_sgio2audio *chip = priv;
+	int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
+	       CODEC_CONTROL_READ, &mace->perif.audio.codec_control);
+	wmb();
+	val = readq(&mace->perif.audio.codec_control); /* flush bus */
+	udelay(200);
+
+	val = readq(&mace->perif.audio.codec_read);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+	return val;
+}
+
+/*
+ * write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
+ */
+static int write_ad1843_reg(void *priv, int reg, int word)
+{
+	struct snd_sgio2audio *chip = priv;
+	int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
+	       (word << CODEC_CONTROL_WORD_SHIFT),
+	       &mace->perif.audio.codec_control);
+	wmb();
+	val = readq(&mace->perif.audio.codec_control); /* flush bus */
+	udelay(200);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+	return 0;
+}
+
+static int sgio2audio_gain_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843,
+					     (int)kcontrol->private_value);
+	return 0;
+}
+
+static int sgio2audio_gain_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+	int vol;
+
+	vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value);
+
+	ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF;
+	ucontrol->value.integer.value[1] = vol & 0xFF;
+
+	return 0;
+}
+
+static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+	int newvol, oldvol;
+
+	oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value);
+	newvol = (ucontrol->value.integer.value[0] << 8) |
+		ucontrol->value.integer.value[1];
+
+	newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value,
+		newvol);
+
+	return newvol != oldvol;
+}
+
+static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	static const char *texts[3] = {
+		"Cam Mic", "Mic", "Line"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item >= 3)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843);
+	return 0;
+}
+
+static int sgio2audio_source_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+	int newsrc, oldsrc;
+
+	oldsrc = ad1843_get_recsrc(&chip->ad1843);
+	newsrc = ad1843_set_recsrc(&chip->ad1843,
+				   ucontrol->value.enumerated.item[0]);
+
+	return newsrc != oldsrc;
+}
+
+/* dac1/pcm0 mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "PCM Playback Volume",
+	.index          = 0,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_PCM_0,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* dac2/pcm1 mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "PCM Playback Volume",
+	.index          = 1,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_PCM_1,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* record level mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Capture Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_RECLEV,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* record level source control */
+static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Capture Source",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info           = sgio2audio_source_info,
+	.get            = sgio2audio_source_get,
+	.put            = sgio2audio_source_put,
+};
+
+/* line mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Line Playback Volume",
+	.index          = 0,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_LINE,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* cd mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Line Playback Volume",
+	.index          = 1,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_LINE_2,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* mic mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Mic Playback Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_MIC,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+
+static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
+{
+	int err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_recsource, chip));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_line, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_cd, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_mic, chip));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/* low-level audio interface DMA */
+
+/* get data out of bounce buffer, count must be a multiple of 32 */
+/* returns 1 if a period has elapsed */
+static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
+					unsigned int ch, unsigned int count)
+{
+	int ret;
+	unsigned long src_base, src_pos, dst_mask;
+	unsigned char *dst_base;
+	int dst_pos;
+	u64 *src;
+	s16 *dst;
+	u64 x;
+	unsigned long flags;
+	struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
+
+	spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+	src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT);
+	src_pos = readq(&mace->perif.audio.chan[ch].read_ptr);
+	dst_base = runtime->dma_area;
+	dst_pos = chip->channel[ch].pos;
+	dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
+
+	/* check if a period has elapsed */
+	chip->channel[ch].size += (count >> 3); /* in frames */
+	ret = chip->channel[ch].size >= runtime->period_size;
+	chip->channel[ch].size %= runtime->period_size;
+
+	while (count) {
+		src = (u64 *)(src_base + src_pos);
+		dst = (s16 *)(dst_base + dst_pos);
+
+		x = *src;
+		dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff;
+		dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff;
+
+		src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK;
+		dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask;
+		count -= sizeof(u64);
+	}
+
+	writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */
+	chip->channel[ch].pos = dst_pos;
+
+	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+	return ret;
+}
+
+/* put some DMA data in bounce buffer, count must be a multiple of 32 */
+/* returns 1 if a period has elapsed */
+static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
+					unsigned int ch, unsigned int count)
+{
+	int ret;
+	s64 l, r;
+	unsigned long dst_base, dst_pos, src_mask;
+	unsigned char *src_base;
+	int src_pos;
+	u64 *dst;
+	s16 *src;
+	unsigned long flags;
+	struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
+
+	spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+	dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT);
+	dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr);
+	src_base = runtime->dma_area;
+	src_pos = chip->channel[ch].pos;
+	src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
+
+	/* check if a period has elapsed */
+	chip->channel[ch].size += (count >> 3); /* in frames */
+	ret = chip->channel[ch].size >= runtime->period_size;
+	chip->channel[ch].size %= runtime->period_size;
+
+	while (count) {
+		src = (s16 *)(src_base + src_pos);
+		dst = (u64 *)(dst_base + dst_pos);
+
+		l = src[0]; /* sign extend */
+		r = src[1]; /* sign extend */
+
+		*dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) |
+			((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT);
+
+		dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK;
+		src_pos = (src_pos + 2 * sizeof(s16)) & src_mask;
+		count -= sizeof(u64);
+	}
+
+	writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */
+	chip->channel[ch].pos = src_pos;
+
+	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+	return ret;
+}
+
+static int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+	int ch = chan->idx;
+
+	/* reset DMA channel */
+	writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control);
+	udelay(10);
+	writeq(0, &mace->perif.audio.chan[ch].control);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* push a full buffer */
+		snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32);
+	}
+	/* set DMA to wake on 50% empty and enable interrupt */
+	writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50,
+	       &mace->perif.audio.chan[ch].control);
+	return 0;
+}
+
+static int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+
+	writeq(0, &mace->perif.audio.chan[chan->idx].control);
+	return 0;
+}
+
+static irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id)
+{
+	struct snd_sgio2audio_chan *chan = dev_id;
+	struct snd_pcm_substream *substream;
+	struct snd_sgio2audio *chip;
+	int count, ch;
+
+	substream = chan->substream;
+	chip = snd_pcm_substream_chip(substream);
+	ch = chan->idx;
+
+	/* empty the ring */
+	count = CHANNEL_RING_SIZE -
+		readq(&mace->perif.audio.chan[ch].depth) - 32;
+	if (snd_sgio2audio_dma_pull_frag(chip, ch, count))
+		snd_pcm_period_elapsed(substream);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id)
+{
+	struct snd_sgio2audio_chan *chan = dev_id;
+	struct snd_pcm_substream *substream;
+	struct snd_sgio2audio *chip;
+	int count, ch;
+
+	substream = chan->substream;
+	chip = snd_pcm_substream_chip(substream);
+	ch = chan->idx;
+	/* fill the ring */
+	count = CHANNEL_RING_SIZE -
+		readq(&mace->perif.audio.chan[ch].depth) - 32;
+	if (snd_sgio2audio_dma_push_frag(chip, ch, count))
+		snd_pcm_period_elapsed(substream);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id)
+{
+	struct snd_sgio2audio_chan *chan = dev_id;
+	struct snd_pcm_substream *substream;
+
+	substream = chan->substream;
+	snd_sgio2audio_dma_stop(substream);
+	snd_sgio2audio_dma_start(substream);
+	return IRQ_HANDLED;
+}
+
+/* PCM part */
+/* PCM hardware definition */
+static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
+	.rates =            SNDRV_PCM_RATE_8000_48000,
+	.rate_min =         8000,
+	.rate_max =         48000,
+	.channels_min =     2,
+	.channels_max =     2,
+	.buffer_bytes_max = 65536,
+	.period_bytes_min = 32768,
+	.period_bytes_max = 65536,
+	.periods_min =      1,
+	.periods_max =      1024,
+};
+
+/* PCM playback open callback */
+static int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sgio2audio_pcm_hw;
+	runtime->private_data = &chip->channel[1];
+	return 0;
+}
+
+static int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sgio2audio_pcm_hw;
+	runtime->private_data = &chip->channel[2];
+	return 0;
+}
+
+/* PCM capture open callback */
+static int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sgio2audio_pcm_hw;
+	runtime->private_data = &chip->channel[0];
+	return 0;
+}
+
+/* PCM close callback */
+static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->private_data = NULL;
+	return 0;
+}
+
+
+/* hw_params callback */
+static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int size = params_buffer_bytes(hw_params);
+
+	/* alloc virtual 'dma' area */
+	if (runtime->dma_area)
+		vfree(runtime->dma_area);
+	runtime->dma_area = vmalloc(size);
+	if (runtime->dma_area == NULL)
+		return -ENOMEM;
+	runtime->dma_bytes = size;
+	return 0;
+}
+
+/* hw_free callback */
+static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	if (substream->runtime->dma_area)
+		vfree(substream->runtime->dma_area);
+	substream->runtime->dma_area = NULL;
+	return 0;
+}
+
+/* prepare callback */
+static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+	int ch = chan->idx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+	/* Setup the pseudo-dma transfer pointers.  */
+	chip->channel[ch].pos = 0;
+	chip->channel[ch].size = 0;
+	chip->channel[ch].substream = substream;
+
+	/* set AD1843 format */
+	/* hardware format is always S16_LE */
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ad1843_setup_dac(&chip->ad1843,
+				 ch - 1,
+				 runtime->rate,
+				 SNDRV_PCM_FORMAT_S16_LE,
+				 runtime->channels);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ad1843_setup_adc(&chip->ad1843,
+				 runtime->rate,
+				 SNDRV_PCM_FORMAT_S16_LE,
+				 runtime->channels);
+		break;
+	}
+	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+	return 0;
+}
+
+/* trigger callback */
+static int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream,
+				      int cmd)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* start the PCM engine */
+		snd_sgio2audio_dma_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		/* stop the PCM engine */
+		snd_sgio2audio_dma_stop(substream);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+
+	/* get the current hardware pointer */
+	return bytes_to_frames(substream->runtime,
+			       chip->channel[chan->idx].pos);
+}
+
+/* get the physical page pointer on the given offset */
+static struct page *snd_sgio2audio_page(struct snd_pcm_substream *substream,
+					unsigned long offset)
+{
+	return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+
+/* operators */
+static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
+	.open =        snd_sgio2audio_playback1_open,
+	.close =       snd_sgio2audio_pcm_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_sgio2audio_pcm_hw_params,
+	.hw_free =     snd_sgio2audio_pcm_hw_free,
+	.prepare =     snd_sgio2audio_pcm_prepare,
+	.trigger =     snd_sgio2audio_pcm_trigger,
+	.pointer =     snd_sgio2audio_pcm_pointer,
+	.page =        snd_sgio2audio_page,
+};
+
+static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
+	.open =        snd_sgio2audio_playback2_open,
+	.close =       snd_sgio2audio_pcm_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_sgio2audio_pcm_hw_params,
+	.hw_free =     snd_sgio2audio_pcm_hw_free,
+	.prepare =     snd_sgio2audio_pcm_prepare,
+	.trigger =     snd_sgio2audio_pcm_trigger,
+	.pointer =     snd_sgio2audio_pcm_pointer,
+	.page =        snd_sgio2audio_page,
+};
+
+static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
+	.open =        snd_sgio2audio_capture_open,
+	.close =       snd_sgio2audio_pcm_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_sgio2audio_pcm_hw_params,
+	.hw_free =     snd_sgio2audio_pcm_hw_free,
+	.prepare =     snd_sgio2audio_pcm_prepare,
+	.trigger =     snd_sgio2audio_pcm_trigger,
+	.pointer =     snd_sgio2audio_pcm_pointer,
+	.page =        snd_sgio2audio_page,
+};
+
+/*
+ *  definitions of capture are omitted here...
+ */
+
+/* create a pcm device */
+static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	/* create first pcm device with one outputs and one input */
+	err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SGI O2 DAC1");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_sgio2audio_playback1_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&snd_sgio2audio_capture_ops);
+
+	/* create second  pcm device with one outputs and no input */
+	err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SGI O2 DAC2");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_sgio2audio_playback2_ops);
+
+	return 0;
+}
+
+static struct {
+	int idx;
+	int irq;
+	irqreturn_t (*isr)(int, void *);
+	const char *desc;
+} snd_sgio2_isr_table[] = {
+	{
+		.idx = 0,
+		.irq = MACEISA_AUDIO1_DMAT_IRQ,
+		.isr = snd_sgio2audio_dma_in_isr,
+		.desc = "Capture DMA Channel 0"
+	}, {
+		.idx = 0,
+		.irq = MACEISA_AUDIO1_OF_IRQ,
+		.isr = snd_sgio2audio_error_isr,
+		.desc = "Capture Overflow"
+	}, {
+		.idx = 1,
+		.irq = MACEISA_AUDIO2_DMAT_IRQ,
+		.isr = snd_sgio2audio_dma_out_isr,
+		.desc = "Playback DMA Channel 1"
+	}, {
+		.idx = 1,
+		.irq = MACEISA_AUDIO2_MERR_IRQ,
+		.isr = snd_sgio2audio_error_isr,
+		.desc = "Memory Error Channel 1"
+	}, {
+		.idx = 2,
+		.irq = MACEISA_AUDIO3_DMAT_IRQ,
+		.isr = snd_sgio2audio_dma_out_isr,
+		.desc = "Playback DMA Channel 2"
+	}, {
+		.idx = 2,
+		.irq = MACEISA_AUDIO3_MERR_IRQ,
+		.isr = snd_sgio2audio_error_isr,
+		.desc = "Memory Error Channel 2"
+	}
+};
+
+/* ALSA driver */
+
+static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
+{
+	int i;
+
+	/* reset interface */
+	writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
+	udelay(1);
+	writeq(0, &mace->perif.audio.control);
+
+	/* release IRQ's */
+	for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++)
+		free_irq(snd_sgio2_isr_table[i].irq,
+			 &chip->channel[snd_sgio2_isr_table[i].idx]);
+
+	dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+			  chip->ring_base, chip->ring_base_dma);
+
+	/* release card data */
+	kfree(chip);
+	return 0;
+}
+
+static int snd_sgio2audio_dev_free(struct snd_device *device)
+{
+	struct snd_sgio2audio *chip = device->device_data;
+
+	return snd_sgio2audio_free(chip);
+}
+
+static struct snd_device_ops ops = {
+	.dev_free = snd_sgio2audio_dev_free,
+};
+
+static int __devinit snd_sgio2audio_create(struct snd_card *card,
+					   struct snd_sgio2audio **rchip)
+{
+	struct snd_sgio2audio *chip;
+	int i, err;
+
+	*rchip = NULL;
+
+	/* check if a codec is attached to the interface */
+	/* (Audio or Audio/Video board present) */
+	if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT))
+		return -ENOENT;
+
+	chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+					     &chip->ring_base_dma, GFP_USER);
+	if (chip->ring_base == NULL) {
+		printk(KERN_ERR
+		       "sgio2audio: could not allocate ring buffers\n");
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&chip->ad1843_lock);
+
+	/* initialize channels */
+	for (i = 0; i < 3; i++) {
+		spin_lock_init(&chip->channel[i].lock);
+		chip->channel[i].idx = i;
+	}
+
+	/* allocate IRQs */
+	for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) {
+		if (request_irq(snd_sgio2_isr_table[i].irq,
+				snd_sgio2_isr_table[i].isr,
+				0,
+				snd_sgio2_isr_table[i].desc,
+				&chip->channel[snd_sgio2_isr_table[i].idx])) {
+			snd_sgio2audio_free(chip);
+			printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n",
+			       snd_sgio2_isr_table[i].irq);
+			return -EBUSY;
+		}
+	}
+
+	/* reset the interface */
+	writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
+	udelay(1);
+	writeq(0, &mace->perif.audio.control);
+	msleep_interruptible(1); /* give time to recover */
+
+	/* set ring base */
+	writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase);
+
+	/* attach the AD1843 codec */
+	chip->ad1843.read = read_ad1843_reg;
+	chip->ad1843.write = write_ad1843_reg;
+	chip->ad1843.chip = chip;
+
+	/* initialize the AD1843 codec */
+	err = ad1843_init(&chip->ad1843);
+	if (err < 0) {
+		snd_sgio2audio_free(chip);
+		return err;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sgio2audio_free(chip);
+		return err;
+	}
+	*rchip = chip;
+	return 0;
+}
+
+static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
+{
+	struct snd_card *card;
+	struct snd_sgio2audio *chip;
+	int err;
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	err = snd_sgio2audio_create(card, &chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_card_set_dev(card, &pdev->dev);
+
+	err = snd_sgio2audio_new_pcm(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	err = snd_sgio2audio_new_mixer(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "SGI O2 Audio");
+	strcpy(card->shortname, "SGI O2 Audio");
+	sprintf(card->longname, "%s irq %i-%i",
+		card->shortname,
+		MACEISA_AUDIO1_DMAT_IRQ,
+		MACEISA_AUDIO3_MERR_IRQ);
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	platform_set_drvdata(pdev, card);
+	return 0;
+}
+
+static int __exit snd_sgio2audio_remove(struct platform_device *pdev)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+
+	snd_card_free(card);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver sgio2audio_driver = {
+	.probe	= snd_sgio2audio_probe,
+	.remove	= __devexit_p(snd_sgio2audio_remove),
+	.driver = {
+		.name	= "sgio2audio",
+		.owner	= THIS_MODULE,
+	}
+};
+
+static int __init alsa_card_sgio2audio_init(void)
+{
+	return platform_driver_register(&sgio2audio_driver);
+}
+
+static void __exit alsa_card_sgio2audio_exit(void)
+{
+	platform_driver_unregister(&sgio2audio_driver);
+}
+
+module_init(alsa_card_sgio2audio_init)
+module_exit(alsa_card_sgio2audio_exit)
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index 3be2dc1025b50592ef29f61e5480e4431a6de103..33940139844bf3893ab5487e711a728682c1c096 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -7,7 +7,7 @@
 
 config SOUND_BCM_CS4297A
 	tristate "Crystal Sound CS4297a (for Swarm)"
-	depends on SOUND_PRIME && SIBYTE_SWARM
+	depends on SIBYTE_SWARM
 	help
 	  The BCM91250A has a Crystal CS4297a on synchronous serial
 	  port B (in addition to the DB-9 serial port).  Say Y or M
@@ -17,7 +17,7 @@ config SOUND_BCM_CS4297A
 
 config SOUND_VWSND
 	tristate "SGI Visual Workstation Sound"
-	depends on SOUND_PRIME && X86_VISWS
+	depends on X86_VISWS
 	help
 	  Say Y or M if you have an SGI Visual Workstation and you want to be
 	  able to use its on-board audio.  Read
@@ -26,19 +26,18 @@ config SOUND_VWSND
 
 config SOUND_HAL2
 	tristate "SGI HAL2 sound (EXPERIMENTAL)"
-	depends on SOUND_PRIME && SGI_IP22 && 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"
-	select SND_AC97_CODEC
-	depends on SOUND_PRIME && (SOC_AU1550 || SOC_AU1200)
+	depends on SOC_AU1550 || SOC_AU1200
 
 config SOUND_TRIDENT
 	tristate "Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core"
-	depends on SOUND_PRIME && PCI
+	depends on PCI
 	---help---
 	  Say Y or M if you have a PCI sound card utilizing the Trident
 	  4DWave-DX/NX chipset or your mother board chipset has SiS 7018
@@ -79,7 +78,7 @@ config SOUND_TRIDENT
 
 config SOUND_MSNDCLAS
 	tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
-	depends on SOUND_PRIME && (m || !STANDALONE) && ISA
+	depends on (m || !STANDALONE) && ISA
 	help
 	  Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
 	  Monterey (not for the Pinnacle or Fiji).
@@ -143,7 +142,7 @@ config MSNDCLAS_IO
 
 config SOUND_MSNDPIN
 	tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji"
-	depends on SOUND_PRIME && (m || !STANDALONE) && ISA
+	depends on (m || !STANDALONE) && ISA
 	help
 	  Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji.
 	  See <file:Documentation/sound/oss/MultiSound> for important information
@@ -229,7 +228,7 @@ config MSNDPIN_NONPNP
 	  configure the card's resources.
 
 comment "MSND Pinnacle DSP section will be configured to above parameters."
-	depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+	depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
 
 config MSNDPIN_CFG
 	hex "MSND Pinnacle config port 250,260,270"
@@ -242,7 +241,7 @@ config MSNDPIN_CFG
 	  Mode".
 
 comment "Pinnacle-specific Device Configuration (0 disables)"
-	depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+	depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
 
 config MSNDPIN_MPU_IO
 	hex "MSND Pinnacle MPU I/O (e.g. 330)"
@@ -294,7 +293,7 @@ config MSNDPIN_JOYSTICK_IO
 
 config MSND_FIFOSIZE
 	int "MSND buffer size (kB)"
-	depends on SOUND_PRIME && (SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y)
+	depends on SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y
 	default "128"
 	help
 	  Configures the size of each audio buffer, in kilobytes, for
@@ -302,9 +301,9 @@ config MSND_FIFOSIZE
 	  and Pinnacle). Larger values reduce the chance of data overruns at
 	  the expense of overall latency. If unsure, use the default.
 
-config SOUND_OSS
+menuconfig SOUND_OSS
 	tristate "OSS sound modules"
-	depends on SOUND_PRIME && ISA_DMA_API && VIRT_TO_BUS
+	depends on ISA_DMA_API && VIRT_TO_BUS
 	help
 	  OSS is the Open Sound System suite of sound card drivers.  They make
 	  sound programming easier since they provide a common API.  Say Y or
@@ -312,16 +311,16 @@ config SOUND_OSS
 	  driver for your sound card above, then pick your driver from the
 	  list below.
 
+if SOUND_OSS
+
 config SOUND_TRACEINIT
 	bool "Verbose initialisation"
-	depends on SOUND_OSS
 	help
 	  Verbose soundcard initialization -- affects the format of autoprobe
 	  and initialization messages at boot time.
 
 config SOUND_DMAP
 	bool "Persistent DMA buffers"
-	depends on SOUND_OSS
 	---help---
 	  Linux can often have problems allocating DMA buffers for ISA sound
 	  cards on machines with more than 16MB of RAM. This is because ISA
@@ -338,8 +337,6 @@ config SOUND_DMAP
 
 config SOUND_SSCAPE
 	tristate "Ensoniq SoundScape support"
-	depends on SOUND_OSS
-	depends on VIRT_TO_BUS
 	help
 	  Answer Y if you have a sound card based on the Ensoniq SoundScape
 	  chipset. Such cards are being manufactured at least by Ensoniq, Spea
@@ -352,13 +349,11 @@ config SOUND_SSCAPE
 
 config SOUND_VMIDI
 	tristate "Loopback MIDI device support"
-	depends on SOUND_OSS
 	help
 	  Support for MIDI loopback on port 1 or 2.
 
 config SOUND_TRIX
 	tristate "MediaTrix AudioTrix Pro support"
-	depends on SOUND_OSS
 	help
 	  Answer Y if you have the AudioTriX Pro sound card manufactured
 	  by MediaTrix.
@@ -382,7 +377,6 @@ config TRIX_BOOT_FILE
 
 config SOUND_MSS
 	tristate "Microsoft Sound System support"
-	depends on SOUND_OSS
 	---help---
 	  Again think carefully before answering Y to this question.  It's
 	  safe to answer Y if you have the original Windows Sound System card
@@ -414,7 +408,6 @@ config SOUND_MSS
 
 config SOUND_MPU401
 	tristate "MPU-401 support (NOT for SB16)"
-	depends on SOUND_OSS
 	---help---
 	  Be careful with this question.  The MPU401 interface is supported by
 	  all sound cards.  However, some natively supported cards have their
@@ -430,7 +423,6 @@ config SOUND_MPU401
 
 config SOUND_PAS
 	tristate "ProAudioSpectrum 16 support"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
 	  16 or Logitech SoundMan 16 sound card. Answer N if you have some
@@ -452,7 +444,6 @@ config PAS_JOYSTICK
 
 config SOUND_PSS
 	tristate "PSS (AD1848, ADSP-2115, ESC614) support"
-	depends on SOUND_OSS
 	help
 	  Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
 	  ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
@@ -495,7 +486,6 @@ config PSS_BOOT_FILE
 
 config SOUND_SB
 	tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y if you have an original Sound Blaster card made by Creative
 	  Labs or a 100% hardware compatible clone (like the Thunderboard or
@@ -522,7 +512,6 @@ config SOUND_SB
 
 config SOUND_YM3812
 	tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
 	  Answering Y is usually a safe and recommended choice, however some
@@ -538,7 +527,6 @@ config SOUND_YM3812
 
 config SOUND_UART6850
 	tristate "6850 UART support"
-	depends on SOUND_OSS
 	help
 	  This option enables support for MIDI interfaces based on the 6850
 	  UART chip. This interface is rarely found on sound cards. It's safe
@@ -549,7 +537,6 @@ config SOUND_UART6850
 
 config SOUND_AEDSP16
 	tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
 	  driver supports Audio Excel DSP 16 but not the III nor PnP versions
@@ -630,14 +617,14 @@ endchoice
 
 config SOUND_VIDC
 	tristate "VIDC 16-bit sound"
-	depends on ARM && (ARCH_ACORN || ARCH_CLPS7500) && SOUND_OSS
+	depends on ARM && (ARCH_ACORN || ARCH_CLPS7500)
 	help
 	  16-bit support for the VIDC onboard sound hardware found on Acorn
 	  machines.
 
 config SOUND_WAVEARTIST
 	tristate "Netwinder WaveArtist"
-	depends on ARM && SOUND_OSS && ARCH_NETWINDER
+	depends on ARM && ARCH_NETWINDER
 	help
 	  Say Y here to include support for the Rockwell WaveArtist sound
 	  system.  This driver is mainly for the NetWinder.
@@ -646,9 +633,11 @@ config SOUND_KAHLUA
 	tristate "XpressAudio Sound Blaster emulation"
 	depends on SOUND_SB
 
+endif	# SOUND_OSS
+
 config SOUND_SH_DAC_AUDIO
 	tristate "SuperH DAC audio support"
-	depends on SOUND_PRIME && CPU_SH3
+	depends on CPU_SH3
 
 config SOUND_SH_DAC_AUDIO_CHANNEL
 	int "DAC channel"
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index a003c0ea9303f800f27e6de612773d0a2f5012e2..95fc5c681755eafff96d7165c1eeb3b9da72a781 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -211,10 +211,6 @@ static int state_unit = -1;
 static int irq_installed;
 #endif /* MODULE */
 
-/* software implemented recording volume! */
-uint software_input_volume = SW_INPUT_VOLUME_SCALE * SW_INPUT_VOLUME_DEFAULT;
-EXPORT_SYMBOL(software_input_volume);
-
 /* control over who can modify resources shared between play/record */
 static mode_t shared_resource_owner;
 static int shared_resources_initialised;
@@ -1188,7 +1184,7 @@ static struct {
 
 /* publish this function for use by low-level code, if required */
 
-char *get_afmt_string(int afmt)
+static char *get_afmt_string(int afmt)
 {
         switch(afmt) {
             case AFMT_MU_LAW:
@@ -1551,4 +1547,3 @@ EXPORT_SYMBOL(dmasound_catchRadius);
 EXPORT_SYMBOL(dmasound_ulaw2dma8);
 EXPORT_SYMBOL(dmasound_alaw2dma8);
 #endif
-EXPORT_SYMBOL(get_afmt_string) ;
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 202e8103dc4da39bffe0d740ae56bef1de2a2ceb..06e9e88e4c05dd9e0735279fd9d4356163034224 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -710,7 +710,7 @@ static MACHINE machAmiga = {
 /*** Config & Setup **********************************************************/
 
 
-int __init dmasound_paula_init(void)
+static int __init dmasound_paula_init(void)
 {
 	int err;
 
diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c
index b3379dd7ca5e1d49d5cc8d0a13c533b891ef8875..1855b14d90c3922989abfd43a0e53b57423174f6 100644
--- a/sound/oss/dmasound/dmasound_q40.c
+++ b/sound/oss/dmasound/dmasound_q40.c
@@ -611,7 +611,7 @@ static MACHINE machQ40 = {
 /*** Config & Setup **********************************************************/
 
 
-int __init dmasound_q40_init(void)
+static int __init dmasound_q40_init(void)
 {
 	if (MACH_IS_Q40) {
 	    dmasound.mach = machQ40;
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
index ba38d6200099f883f3f188d6e9255bf07afa80b6..e4282d93a1aaa121aaac6eeac5c9f22cd92083ec 100644
--- a/sound/oss/msnd.c
+++ b/sound/oss/msnd.c
@@ -20,8 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $
- *
  ********************************************************************/
 
 #include <linux/module.h>
diff --git a/sound/oss/msnd.h b/sound/oss/msnd.h
index d0ca582c4583e302f171c21e653ea4e5c43f7959..61b3955481c56da1f7b88ca2ac43fd08ed5ef15a 100644
--- a/sound/oss/msnd.h
+++ b/sound/oss/msnd.h
@@ -24,8 +24,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.h,v 1.36 1999/03/21 17:05:42 andrewtv Exp $
- *
  ********************************************************************/
 #ifndef __MSND_H
 #define __MSND_H
diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h
index 7ffea5267f9699dd4ca22aa2557f1ca777505c91..1a17dde2f650ea04f14cafd68b7cd8cd086c027b 100644
--- a/sound/oss/msnd_classic.h
+++ b/sound/oss/msnd_classic.h
@@ -24,8 +24,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  * 
- * $Id: msnd_classic.h,v 1.10 1999/03/21 17:36:09 andrewtv Exp $
- *
  ********************************************************************/
 #ifndef __MSND_CLASSIC_H
 #define __MSND_CLASSIC_H
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index f1f49ebf752ea0a6ee109fbf9622b1a0265c92f0..bf27e008f465f29aed345549b583c3e7dcb75213 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -29,13 +29,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $
- *
  * 12-3-2000  Modified IO port validation  Steve Sycamore
  *
- *
- * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $
- *
  ********************************************************************/
 
 #include <linux/kernel.h>
diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h
index cce91148700481b1ac28aa67729311d0ebcbf2e5..c18d66cbbe3fdb0d235174e2f768acf6e34e3918 100644
--- a/sound/oss/msnd_pinnacle.h
+++ b/sound/oss/msnd_pinnacle.h
@@ -24,8 +24,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.h,v 1.11 1999/03/21 17:36:09 andrewtv Exp $
- *
  ********************************************************************/
 #ifndef __MSND_PINNACLE_H
 #define __MSND_PINNACLE_H
diff --git a/sound/parisc/Kconfig b/sound/parisc/Kconfig
index a5a7f9d75d051e37398b094a2e42100adbf19029..9b61d95010f07134e481eed1a481d77d13d8ca88 100644
--- a/sound/parisc/Kconfig
+++ b/sound/parisc/Kconfig
@@ -1,15 +1,20 @@
 # ALSA PA-RISC drivers
 
-menu "GSC devices"
-	depends on SND!=n && GSC
+menuconfig SND_GSC
+	bool "GSC sound devices"
+	depends on GSC
+	default y
+	help
+	  Support for GSC sound devices on PA-RISC architectures.
+
+if SND_GSC
 
 config SND_HARMONY
 	tristate "Harmony/Vivace sound chip"
-	depends on SND
 	select SND_PCM
 	help
 	  Say 'Y' or 'M' to include support for the Harmony/Vivace sound
 	  chip found in most GSC-based PA-RISC workstations.  It's frequently
 	  provided as part of the Lasi multi-function IC.
 
-endmenu
+endif	# SND_GSC
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 7e47421095726afd72650cffe20e2306aebfa252..8fe5dac3942824a8c9274eb1ce0dea74e80f8e99 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -1,11 +1,16 @@
 # ALSA PCI drivers
 
-menu "PCI devices"
-	depends on SND!=n && PCI
+menuconfig SND_PCI
+	bool "PCI sound devices"
+	depends on PCI
+	default y
+	help
+	  Support for sound devices connected via the PCI bus.
+
+if SND_PCI
 
 config SND_AD1889
 	tristate "Analog Devices AD1889"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated AC97 sound
@@ -17,7 +22,6 @@ config SND_AD1889
 
 config SND_ALS300
 	tristate "Avance Logic ALS300/ALS300+"
-	depends on SND
 	select SND_PCM
 	select SND_AC97_CODEC
 	select SND_OPL3_LIB
@@ -29,7 +33,7 @@ config SND_ALS300
 
 config SND_ALS4000
 	tristate "Avance Logic ALS4000"
-	depends on SND && ISA_DMA_API
+	depends on ISA_DMA_API
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -43,7 +47,6 @@ config SND_ALS4000
 
 config SND_ALI5451
 	tristate "ALi M5451 PCI Audio Controller"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -57,7 +60,6 @@ config SND_ALI5451
 
 config SND_ATIIXP
 	tristate "ATI IXP AC97 Controller"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated AC97 sound
@@ -69,7 +71,6 @@ config SND_ATIIXP
 
 config SND_ATIIXP_MODEM
 	tristate "ATI IXP Modem"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated MC97 modem on
@@ -80,7 +81,6 @@ config SND_ATIIXP_MODEM
 
 config SND_AU8810
 	tristate "Aureal Advantage"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -95,7 +95,6 @@ config SND_AU8810
 
 config SND_AU8820
 	tristate "Aureal Vortex"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -109,7 +108,6 @@ config SND_AU8820
 
 config SND_AU8830
 	tristate "Aureal Vortex 2"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -124,7 +122,6 @@ config SND_AU8830
 
 config SND_AW2
 	tristate "Emagic Audiowerk 2"
-	depends on SND
 	help
 	  Say Y here to include support for Emagic Audiowerk 2 soundcards.
 
@@ -139,7 +136,7 @@ config SND_AW2
 
 config SND_AZT3328
 	tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
-	depends on SND && EXPERIMENTAL
+	depends on EXPERIMENTAL
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -152,7 +149,6 @@ config SND_AZT3328
 
 config SND_BT87X
 	tristate "Bt87x Audio Capture"
-	depends on SND
 	select SND_PCM
 	help
 	  If you want to record audio from TV cards based on
@@ -174,7 +170,6 @@ config SND_BT87X_OVERCLOCK
 
 config SND_CA0106
 	tristate "SB Audigy LS / Live 24bit"
-	depends on SND
 	select SND_AC97_CODEC
 	select SND_RAWMIDI
 	select SND_VMASTER
@@ -187,7 +182,6 @@ config SND_CA0106
 
 config SND_CMIPCI
 	tristate "C-Media 8338, 8738, 8768, 8770"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -201,13 +195,11 @@ config SND_CMIPCI
 
 config SND_OXYGEN_LIB
         tristate
-	depends on SND
 	select SND_PCM
 	select SND_MPU401_UART
 
 config SND_OXYGEN
 	tristate "C-Media 8788 (Oxygen)"
-	depends on SND
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for sound cards based on the
@@ -225,7 +217,6 @@ config SND_OXYGEN
 
 config SND_CS4281
 	tristate "Cirrus Logic (Sound Fusion) CS4281"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
@@ -237,7 +228,6 @@ config SND_CS4281
 
 config SND_CS46XX
 	tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
 	help
@@ -258,7 +248,7 @@ config SND_CS46XX_NEW_DSP
 
 config SND_CS5530
 	tristate "CS5530 Audio"
-	depends on SND && ISA_DMA_API
+	depends on ISA_DMA_API
 	select SND_SB16_DSP
 	help
 	  Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
@@ -268,7 +258,7 @@ config SND_CS5530
 
 config SND_CS5535AUDIO
 	tristate "CS5535/CS5536 Audio"
-	depends on SND && X86 && !X86_64
+	depends on X86 && !X86_64
 	select SND_PCM
 	select SND_AC97_CODEC
 	help
@@ -286,7 +276,6 @@ config SND_CS5535AUDIO
 
 config SND_DARLA20
 	tristate "(Echoaudio) Darla20"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -297,7 +286,6 @@ config SND_DARLA20
 
 config SND_GINA20
 	tristate "(Echoaudio) Gina20"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -308,7 +296,6 @@ config SND_GINA20
 
 config SND_LAYLA20
 	tristate "(Echoaudio) Layla20"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -320,7 +307,6 @@ config SND_LAYLA20
 
 config SND_DARLA24
 	tristate "(Echoaudio) Darla24"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -331,7 +317,6 @@ config SND_DARLA24
 
 config SND_GINA24
 	tristate "(Echoaudio) Gina24"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -342,7 +327,6 @@ config SND_GINA24
 
 config SND_LAYLA24
 	tristate "(Echoaudio) Layla24"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -354,7 +338,6 @@ config SND_LAYLA24
 
 config SND_MONA
 	tristate "(Echoaudio) Mona"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -366,7 +349,6 @@ config SND_MONA
 
 config SND_MIA
 	tristate "(Echoaudio) Mia"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -378,7 +360,6 @@ config SND_MIA
 
 config SND_ECHO3G
 	tristate "(Echoaudio) 3G cards"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -390,7 +371,6 @@ config SND_ECHO3G
 
 config SND_INDIGO
 	tristate "(Echoaudio) Indigo"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -401,7 +381,6 @@ config SND_INDIGO
 
 config SND_INDIGOIO
 	tristate "(Echoaudio) Indigo IO"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -412,7 +391,6 @@ config SND_INDIGOIO
 
 config SND_INDIGODJ
 	tristate "(Echoaudio) Indigo DJ"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -423,7 +401,6 @@ config SND_INDIGODJ
 
 config SND_EMU10K1
 	tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
-	depends on SND
 	select FW_LOADER
 	select SND_HWDEP
 	select SND_RAWMIDI
@@ -441,7 +418,6 @@ config SND_EMU10K1
 
 config SND_EMU10K1X
 	tristate "Emu10k1X (Dell OEM Version)"
-	depends on SND
 	select SND_AC97_CODEC
 	select SND_RAWMIDI
 	help
@@ -453,7 +429,6 @@ config SND_EMU10K1X
 
 config SND_ENS1370
 	tristate "(Creative) Ensoniq AudioPCI 1370"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_PCM
 	help
@@ -464,7 +439,6 @@ config SND_ENS1370
 
 config SND_ENS1371
 	tristate "(Creative) Ensoniq AudioPCI 1371/1373"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
 	help
@@ -476,7 +450,6 @@ config SND_ENS1371
 
 config SND_ES1938
 	tristate "ESS ES1938/1946/1969 (Solo-1)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
@@ -489,7 +462,6 @@ config SND_ES1938
 
 config SND_ES1968
 	tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -501,7 +473,6 @@ config SND_ES1968
 
 config SND_FM801
 	tristate "ForteMedia FM801"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
@@ -528,7 +499,6 @@ config SND_FM801_TEA575X
 
 config SND_HDA_INTEL
 	tristate "Intel HD Audio"
-	depends on SND
 	select SND_PCM
 	select SND_VMASTER
 	help
@@ -637,7 +607,6 @@ config SND_HDA_POWER_SAVE_DEFAULT
 
 config SND_HDSP
 	tristate "RME Hammerfall DSP Audio"
-	depends on SND
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -650,7 +619,6 @@ config SND_HDSP
 
 config SND_HDSPM
 	tristate "RME Hammerfall DSP MADI"
-	depends on SND
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -663,7 +631,6 @@ config SND_HDSPM
 
 config SND_HIFIER
 	tristate "TempoTec HiFier Fantasia"
-	depends on SND
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for the MediaTek/TempoTec HiFier
@@ -674,7 +641,6 @@ config SND_HIFIER
 
 config SND_ICE1712
 	tristate "ICEnsemble ICE1712 (Envy24)"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -691,8 +657,7 @@ config SND_ICE1712
 
 config SND_ICE1724
 	tristate "ICE/VT1724/1720 (Envy24HT/PT)"
-	depends on SND
-	select SND_MPU401_UART
+	select SND_RAWMIDI
 	select SND_AC97_CODEC
 	select SND_VMASTER
 	help
@@ -709,7 +674,6 @@ config SND_ICE1724
 
 config SND_INTEL8X0
 	tristate "Intel/SiS/nVidia/AMD/ALi AC97 Controller"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated AC97 sound
@@ -722,7 +686,6 @@ config SND_INTEL8X0
 
 config SND_INTEL8X0M
 	tristate "Intel/SiS/nVidia/AMD MC97 Modem"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated MC97 modem on
@@ -733,7 +696,6 @@ config SND_INTEL8X0M
 
 config SND_KORG1212
 	tristate "Korg 1212 IO"
-	depends on SND
 	select FW_LOADER if !SND_KORG1212_FIRMWARE_IN_KERNEL
 	select SND_PCM
 	help
@@ -753,7 +715,6 @@ config SND_KORG1212_FIRMWARE_IN_KERNEL
 
 config SND_MAESTRO3
 	tristate "ESS Allegro/Maestro3"
-	depends on SND
 	select FW_LOADER if !SND_MAESTRO3_FIRMWARE_IN_KERNEL
 	select SND_AC97_CODEC
 	help
@@ -774,7 +735,6 @@ config SND_MAESTRO3_FIRMWARE_IN_KERNEL
 
 config SND_MIXART
 	tristate "Digigram miXart"
-	depends on SND
 	select SND_HWDEP
 	select SND_PCM
 	help
@@ -786,7 +746,6 @@ config SND_MIXART
 
 config SND_NM256
 	tristate "NeoMagic NM256AV/ZX"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for NeoMagic NM256AV/ZX chips.
@@ -796,7 +755,6 @@ config SND_NM256
 
 config SND_PCXHR
 	tristate "Digigram PCXHR"
-	depends on SND
 	select SND_PCM
 	select SND_HWDEP
 	help
@@ -807,7 +765,6 @@ config SND_PCXHR
 
 config SND_RIPTIDE
 	tristate "Conexant Riptide"
-	depends on SND
 	select FW_LOADER
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -820,7 +777,6 @@ config SND_RIPTIDE
 
 config SND_RME32
 	tristate "RME Digi32, 32/8, 32 PRO"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y to include support for RME Digi32, Digi32 PRO and
@@ -832,7 +788,6 @@ config SND_RME32
 
 config SND_RME96
 	tristate "RME Digi96, 96/8, 96/8 PRO"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include support for RME Digi96, Digi96/8 and
@@ -843,7 +798,6 @@ config SND_RME96
 
 config SND_RME9652
 	tristate "RME Digi9652 (Hammerfall)"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include support for RME Hammerfall (RME
@@ -854,7 +808,7 @@ config SND_RME9652
 
 config SND_SIS7019
 	tristate "SiS 7019 Audio Accelerator"
-	depends on SND && X86 && !X86_64
+	depends on X86 && !X86_64
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the SiS 7019 Audio Accelerator.
@@ -864,7 +818,6 @@ config SND_SIS7019
 
 config SND_SONICVIBES
 	tristate "S3 SonicVibes"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
@@ -877,7 +830,6 @@ config SND_SONICVIBES
 
 config SND_TRIDENT
 	tristate "Trident 4D-Wave DX/NX; SiS 7018"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -889,7 +841,6 @@ config SND_TRIDENT
 
 config SND_VIA82XX
 	tristate "VIA 82C686A/B, 8233/8235 AC97 Controller"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -901,7 +852,6 @@ config SND_VIA82XX
 
 config SND_VIA82XX_MODEM
 	tristate "VIA 82C686A/B, 8233 based Modems"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated MC97 modem on
@@ -912,7 +862,6 @@ config SND_VIA82XX_MODEM
 
 config SND_VIRTUOSO
 	tristate "Asus Virtuoso 100/200 (Xonar)"
-	depends on SND
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for sound cards based on the
@@ -923,7 +872,6 @@ config SND_VIRTUOSO
 
 config SND_VX222
 	tristate "Digigram VX222"
-	depends on SND
 	select SND_VX_LIB
 	help
 	  Say Y here to include support for Digigram VX222 soundcards.
@@ -933,7 +881,6 @@ config SND_VX222
 
 config SND_YMFPCI
 	tristate "Yamaha YMF724/740/744/754"
-	depends on SND
 	select FW_LOADER if !SND_YMFPCI_FIRMWARE_IN_KERNEL
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -954,25 +901,4 @@ config SND_YMFPCI_FIRMWARE_IN_KERNEL
 	  for the YMFPCI driver.  If you choose N here, you need to
 	  install the firmware files from the alsa-firmware package.
 
-config SND_AC97_POWER_SAVE
-	bool "AC97 Power-Saving Mode"
-	depends on SND_AC97_CODEC && EXPERIMENTAL
-	default n
-	help
-	  Say Y here to enable the aggressive power-saving support of
-	  AC97 codecs.  In this mode, the power-mode is dynamically
-	  controlled at each open/close.
-
-	  The mode is activated by passing power_save=1 option to
-	  snd-ac97-codec driver.  You can toggle it dynamically over
-	  sysfs, too.
-
-config SND_AC97_POWER_SAVE_DEFAULT
-	int "Default time-out for AC97 power-save mode"
-	depends on SND_AC97_POWER_SAVE
-	default 0
-	help
-	  The default time-out value in seconds for AC97 automatic
-	  power-save mode.  0 means to disable the power-save mode.
-
-endmenu
+endif	# SND_PCI
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 85ef14bc805649611b51873d8ff7de6de9ba4d6c..65b25d221cd2795135f265dcbb05d47f1dfc8a8f 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -13,7 +13,7 @@ snd-bt87x-objs := bt87x.o
 snd-cmipci-objs := cmipci.o
 snd-cs4281-objs := cs4281.o
 snd-cs5530-objs := cs5530.o
-snd-ens1370-objs := ens1370.o
+snd-ens1370-objs := ens1370.o ak4531_codec.o
 snd-ens1371-objs := ens1371.o
 snd-es1938-objs := es1938.o
 snd-es1968-objs := es1968.o
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index 0be48b1a22d04c018e1434d9710b0f8981468466..41fa322f0971677382fcb9ab3ba94492139ee20c 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/Makefile
@@ -3,16 +3,8 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
 #
 
-snd-ac97-codec-objs := ac97_codec.o ac97_pcm.o
-
-ifneq ($(CONFIG_PROC_FS),)
-snd-ac97-codec-objs += ac97_proc.o
-endif
-
-snd-ak4531-codec-objs := ak4531_codec.o
+snd-ac97-codec-y := ac97_codec.o ac97_pcm.o
+snd-ac97-codec-$(CONFIG_PROC_FS) += ac97_proc.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o
-obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o
-
-obj-m := $(sort $(obj-m))
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 45fd29017ddd59ded7e731ece20c977a4a4bde20..07364c00768a4dd90f8eceeb338d899955a17685 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -49,8 +49,9 @@ MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
 
 #ifdef CONFIG_SND_AC97_POWER_SAVE
 static int power_save = CONFIG_SND_AC97_POWER_SAVE_DEFAULT;
-module_param(power_save, bool, 0644);
-MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+		 "(in second, 0 = disable).");
 #endif
 /*
 
@@ -2294,9 +2295,11 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97)
 	power |= AC97_PD_PR0 | AC97_PD_PR1;	/* ADC & DAC powerdown */
 	snd_ac97_write(ac97, AC97_POWERDOWN, power);
 	udelay(100);
-	power |= AC97_PD_PR2 | AC97_PD_PR3;	/* Analog Mixer powerdown */
+	power |= AC97_PD_PR2;	/* Analog Mixer powerdown (Vref on) */
 	snd_ac97_write(ac97, AC97_POWERDOWN, power);
 	if (ac97_is_power_save_mode(ac97)) {
+		power |= AC97_PD_PR3;	/* Analog Mixer powerdown */
+		snd_ac97_write(ac97, AC97_POWERDOWN, power);
 		udelay(100);
 		/* AC-link powerdown, internal Clk disable */
 		/* FIXME: this may cause click noises on some boards */
@@ -2362,7 +2365,7 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
 		 *  that open/close frequently)
 		 */
 		schedule_delayed_work(&ac97->power_work,
-				      msecs_to_jiffies(2000));
+				      msecs_to_jiffies(power_save * 1000));
 	else {
 		cancel_delayed_work(&ac97->power_work);
 		update_power_regs(ac97);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 1292dcee072df21b34aa39daecaee841a429dfe0..0746e9ccc20b1572ec1cae300b0178c1ad6f90e9 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -669,6 +669,7 @@ AC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
 AC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
 AC97_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
 
+AC97_SINGLE("Master Left Inv Switch", AC97_MASTER, 6, 1, 0),
 AC97_SINGLE("Master ZC Switch", AC97_MASTER, 7, 1, 0),
 AC97_SINGLE("Headphone ZC Switch", AC97_HEADPHONE, 7, 1, 0),
 AC97_SINGLE("Mono ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
@@ -3352,8 +3353,66 @@ AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0),
 AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
 };
 
+static const char *slave_vols_vt1616[] = {
+	"Front Playback Volume",
+	"Surround Playback Volume",
+	"Center Playback Volume",
+	"LFE Playback Volume",
+	NULL
+};
+
+static const char *slave_sws_vt1616[] = {
+	"Front Playback Switch",
+	"Surround Playback Switch",
+	"Center Playback Switch",
+	"LFE Playback Switch",
+	NULL
+};
+
+/* find a mixer control element with the given name */
+static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
+						    const char *name)
+{
+	struct snd_ctl_elem_id id;
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, name);
+	return snd_ctl_find_id(ac97->bus->card, &id);
+}
+
+/* create a virtual master control and add slaves */
+int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
+			 const unsigned int *tlv, const char **slaves)
+{
+	struct snd_kcontrol *kctl;
+	const char **s;
+	int err;
+
+	kctl = snd_ctl_make_virtual_master(name, tlv);
+	if (!kctl)
+		return -ENOMEM;
+	err = snd_ctl_add(ac97->bus->card, kctl);
+	if (err < 0)
+		return err;
+
+	for (s = slaves; *s; s++) {
+		struct snd_kcontrol *sctl;
+
+		sctl = snd_ac97_find_mixer_ctl(ac97, *s);
+		if (!sctl) {
+			snd_printdd("Cannot find slave %s, skipped\n", *s);
+			continue;
+		}
+		err = snd_ctl_add_slave(kctl, sctl);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
 static int patch_vt1616_specific(struct snd_ac97 * ac97)
 {
+	struct snd_kcontrol *kctl;
 	int err;
 
 	if (snd_ac97_try_bit(ac97, 0x5a, 9))
@@ -3361,6 +3420,24 @@ static int patch_vt1616_specific(struct snd_ac97 * ac97)
 			return err;
 	if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
 		return err;
+
+	/* There is already a misnamed master switch.  Rename it.  */
+	kctl = snd_ac97_find_mixer_ctl(ac97, "Master Playback Volume");
+	if (!kctl)
+		return -EINVAL;
+
+	snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Front Playback");
+
+	err = snd_ac97_add_vmaster(ac97, "Master Playback Volume",
+				   kctl->tlv.p, slave_vols_vt1616);
+	if (err < 0)
+		return err;
+
+	err = snd_ac97_add_vmaster(ac97, "Master Playback Switch",
+				   NULL, slave_sws_vt1616);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -3633,7 +3710,7 @@ static int patch_ucb1400(struct snd_ac97 * ac97)
 {
 	ac97->build_ops = &patch_ucb1400_ops;
 	/* enable headphone driver and smart low power mode by default */
-	snd_ac97_write(ac97, 0x6a, 0x0050);
-	snd_ac97_write(ac97, 0x6c, 0x0030);
+	snd_ac97_write_cache(ac97, 0x6a, 0x0050);
+	snd_ac97_write_cache(ac97, 0x6c, 0x0030);
 	return 0;
 }
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ak4531_codec.c
similarity index 96%
rename from sound/pci/ac97/ak4531_codec.c
rename to sound/pci/ak4531_codec.c
index c0c1633999eaf60567d0f41752d3528312561e71..33d37b1c42fcc4354919615a43747655fcdafe4b 100644
--- a/sound/pci/ac97/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -28,9 +28,11 @@
 #include <sound/ak4531_codec.h>
 #include <sound/tlv.h>
 
+/*
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("Universal routines for AK4531 codec");
 MODULE_LICENSE("GPL");
+*/
 
 #ifdef CONFIG_PROC_FS
 static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531);
@@ -270,7 +272,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
 
-static struct snd_kcontrol_new snd_ak4531_controls[] = {
+static struct snd_kcontrol_new snd_ak4531_controls[] __devinitdata = {
 
 AK4531_DOUBLE_TLV("Master Playback Switch", 0,
 		  AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
@@ -379,8 +381,9 @@ static u8 snd_ak4531_initial_map[0x19 + 1] = {
 	0x01		/* 19: Mic Amp Setup */
 };
 
-int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
-		     struct snd_ak4531 **rak4531)
+int __devinit snd_ak4531_mixer(struct snd_card *card,
+			       struct snd_ak4531 *_ak4531,
+			       struct snd_ak4531 **rak4531)
 {
 	unsigned int idx;
 	int err;
@@ -476,7 +479,8 @@ static void snd_ak4531_proc_read(struct snd_info_entry *entry,
 		    ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
 }
 
-static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
+static void __devinit
+snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
 {
 	struct snd_info_entry *entry;
 
@@ -484,25 +488,3 @@ static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak453
 		snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read);
 }
 #endif
-
-EXPORT_SYMBOL(snd_ak4531_mixer);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_ak4531_suspend);
-EXPORT_SYMBOL(snd_ak4531_resume);
-#endif
-
-/*
- *  INIT part
- */
-
-static int __init alsa_ak4531_init(void)
-{
-	return 0;
-}
-
-static void __exit alsa_ak4531_exit(void)
-{
-}
-
-module_init(alsa_ak4531_init)
-module_exit(alsa_ak4531_exit)
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index bc212f41a38a611a84ee679502813377208602e1..e291aa59742e7c31c85e8f220a5248c661916c80 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -1,6 +1,4 @@
 /*
- * $Id: au88x0_game.c,v 1.9 2003/09/22 03:51:28 mjander Exp $
- *
  *  Manuel Jander.
  *
  *  Based on the work of:
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 5f63af6b88a29d35c1c906b6e1e34d88f07780a8..22f18f3cfbc9f45d1b7921aa25ab2399029fe663 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1,6 +1,6 @@
 /*
  *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- *  Copyright (C) 2002, 2005, 2006, 2007 by Andreas Mohr <andi AT lisas.de>
+ *  Copyright (C) 2002, 2005 - 2008 by Andreas Mohr <andi AT lisas.de>
  *
  *  Framework borrowed from Bart Hartgers's als4000.c.
  *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@@ -35,9 +35,20 @@
  *  (3 weeks' worth of evenings filled with driver work).
  *  (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
  *
+ *  It is quite likely that the AZF3328 chip is the PCI cousin of the
+ *  AZF3318 ("azt1020 pnp", "MM Pro 16") ISA chip, given very similar specs.
+ *
  *  The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
- *  for compatibility reasons) has the following features:
+ *  for compatibility reasons) from Azfin (joint-venture of Aztech and Fincitec,
+ *  Fincitec acquired by National Semiconductor in 2002, together with the
+ *  Fincitec-related company ARSmikro) has the following features:
  *
+ *  - compatibility & compliance:
+ *    - Microsoft PC 97 ("PC 97 Hardware Design Guide",
+ *                       http://www.microsoft.com/whdc/archive/pcguides.mspx)
+ *    - Microsoft PC 98 Baseline Audio
+ *    - MPU401 UART
+ *    - Sound Blaster Emulation (DOS Box)
  *  - builtin AC97 conformant codec (SNR over 80dB)
  *    Note that "conformant" != "compliant"!! this chip's mixer register layout
  *    *differs* from the standard AC97 layout:
@@ -48,21 +59,28 @@
  *    addresses illegally. So far unfortunately it looks like the very flexible
  *    ALSA AC97 support is still not enough to easily compensate for such a
  *    grave layout violation despite all tweaks and quirks mechanisms it offers.
- *  - builtin genuine OPL3
+ *  - builtin genuine OPL3 - verified to work fine, 20080506
  *  - full duplex 16bit playback/record at independent sampling rate
- *  - MPU401 (+ legacy address support) FIXME: how to enable legacy addr??
+ *  - MPU401 (+ legacy address support, claimed by one official spec sheet)
+ *    FIXME: how to enable legacy addr??
  *  - game port (legacy address support)
- *  - builtin 3D enhancement (said to be YAMAHA Ymersion)
  *  - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven
- *    features supported)
+ *    features supported). - See common term "Digital Enhanced Game Port"...
+ *    (probably DirectInput 3.0 spec - confirm)
+ *  - builtin 3D enhancement (said to be YAMAHA Ymersion)
  *  - built-in General DirectX timer having a 20 bits counter
  *    with 1us resolution (see below!)
- *  - I2S serial port for external DAC
+ *  - I2S serial output port for external DAC
  *  - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
  *  - supports hardware volume control
  *  - single chip low cost solution (128 pin QFP)
  *  - supports programmable Sub-vendor and Sub-system ID
  *    required for Microsoft's logo compliance (FIXME: where?)
+ *    At least the Trident 4D Wave DX has one bit somewhere
+ *    to enable writes to PCI subsystem VID registers, that should be it.
+ *    This might easily be in extended PCI reg space, since PCI168 also has
+ *    some custom data starting at 0x80. What kind of config settings
+ *    are located in our extended PCI space anyway??
  *  - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
  *
  *  Note that this driver now is actually *better* than the Windows driver,
@@ -74,6 +92,24 @@
  *  - "timidity -iAv -B2,8 -Os -EFreverb=0"
  *  - "pmidi -p 128:0 jazz.mid"
  *
+ *  OPL3 hardware playback testing, try something like:
+ *  cat /proc/asound/hwdep
+ *  and
+ *  aconnect -o
+ *  Then use
+ *  sbiload -Dhw:x,y --opl3 /usr/share/sounds/opl3/std.o3 ......./drums.o3
+ *  where x,y is the xx-yy number as given in hwdep.
+ *  Then try
+ *  pmidi -p a:b jazz.mid
+ *  where a:b is the client number plus 0 usually, as given by aconnect above.
+ *  Oh, and make sure to unmute the FM mixer control (doh!)
+ *  NOTE: power use during OPL3 playback is _VERY_ high (70W --> 90W!)
+ *  despite no CPU activity, possibly due to hindering ACPI idling somehow.
+ *  Shouldn't be a problem of the AZF3328 chip itself, I'd hope.
+ *  Higher PCM / FM mixer levels seem to conflict (causes crackling),
+ *  at least sometimes.   Maybe even use with hardware sequencer timer above :)
+ *  adplay/adplug-utils might soon offer hardware-based OPL3 playback, too.
+ *
  *  Certain PCI versions of this card are susceptible to DMA traffic underruns
  *  in some systems (resulting in sound crackling/clicking/popping),
  *  probably because they don't have a DMA FIFO buffer or so.
@@ -87,6 +123,8 @@
  *  better than a VIA, yet ironically I still get crackling, like many other
  *  people with the same chipset.
  *  Possible remedies:
+ *  - use speaker (amplifier) output instead of headphone output
+ *    (in case crackling is due to overloaded output clipping)
  *  - plug card into a different PCI slot, preferrably one that isn't shared
  *    too much (this helps a lot, but not completely!)
  *  - get rid of PCI VGA card, use AGP instead
@@ -94,18 +132,23 @@
  *  - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
  *    Not too helpful.
  *  - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
- * 
+ *
  * BUGS
- *  - full-duplex might *still* be problematic, not fully tested recently
+ *  - full-duplex might *still* be problematic, however a recent test was fine
  *  - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated
  *    if you set PCM output switch to "pre 3D" instead of "post 3D".
  *    If this can't be set, then get a mixer application that Isn't Stupid (tm)
  *    (e.g. kmix, gamix) - unfortunately several are!!
- * 
+ *  - locking is not entirely clean, especially the audio stream activity
+ *    ints --> may be racy
+ *  - an _unconnected_ secondary joystick at the gameport will be reported
+ *    to be "active" (floating values, not precisely -1) due to the way we need
+ *    to read the Digital Enhanced Game Port. Not sure whether it is fixable.
+ *
  * TODO
  *  - test MPU401 MIDI playback etc.
- *  - add some power micro-management (disable various units of the card
- *    as long as they're unused). However this requires I/O ports which I
+ *  - add more power micro-management (disable various units of the card
+ *    as long as they're unused). However this requires more I/O ports which I
  *    haven't figured out yet and which thus might not even exist...
  *    The standard suspend/resume functionality could probably make use of
  *    some improvement, too...
@@ -113,6 +156,7 @@
  *  - figure out some cleverly evil scheme to possibly make ALSA AC97 code
  *    fully accept our quite incompatible ""AC97"" mixer and thus save some
  *    code (but I'm not too optimistic that doing this is possible at all)
+ *  - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
  */
 
 #include <asm/io.h>
@@ -138,7 +182,7 @@ MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 
 #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
-#define SUPPORT_JOYSTICK 1
+#define SUPPORT_GAMEPORT 1
 #endif
 
 #define DEBUG_MISC	0
@@ -147,13 +191,14 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 #define DEBUG_PLAY_REC	0
 #define DEBUG_IO	0
 #define DEBUG_TIMER	0
+#define DEBUG_GAME	0
 #define MIXER_TESTING	0
 
 #if DEBUG_MISC
 #define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)
 #else
 #define snd_azf3328_dbgmisc(format, args...)
-#endif		
+#endif
 
 #if DEBUG_CALLS
 #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
@@ -163,25 +208,31 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 #define snd_azf3328_dbgcalls(format, args...)
 #define snd_azf3328_dbgcallenter()
 #define snd_azf3328_dbgcallleave()
-#endif		
+#endif
 
 #if DEBUG_MIXER
 #define snd_azf3328_dbgmixer(format, args...) printk(format, ##args)
 #else
 #define snd_azf3328_dbgmixer(format, args...)
-#endif		
+#endif
 
 #if DEBUG_PLAY_REC
 #define snd_azf3328_dbgplay(format, args...) printk(KERN_ERR format, ##args)
 #else
 #define snd_azf3328_dbgplay(format, args...)
-#endif		
+#endif
 
 #if DEBUG_MISC
 #define snd_azf3328_dbgtimer(format, args...) printk(KERN_ERR format, ##args)
 #else
 #define snd_azf3328_dbgtimer(format, args...)
-#endif		
+#endif
+
+#if DEBUG_GAME
+#define snd_azf3328_dbggame(format, args...) printk(KERN_ERR format, ##args)
+#else
+#define snd_azf3328_dbggame(format, args...)
+#endif
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 module_param_array(index, int, NULL, 0444);
@@ -195,51 +246,62 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card *
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
 
-#ifdef SUPPORT_JOYSTICK
-static int joystick[SNDRV_CARDS];
-module_param_array(joystick, bool, NULL, 0444);
-MODULE_PARM_DESC(joystick, "Enable joystick for AZF3328 soundcard.");
-#endif
-
 static int seqtimer_scaling = 128;
 module_param(seqtimer_scaling, int, 0444);
 MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
 
+struct snd_azf3328_audio_stream {
+	struct snd_pcm_substream *substream;
+	int enabled;
+	int running;
+	unsigned long portbase;
+};
+
+enum snd_azf3328_stream_index {
+  AZF_PLAYBACK = 0,
+  AZF_CAPTURE = 1,
+};
+
 struct snd_azf3328 {
 	/* often-used fields towards beginning, then grouped */
-	unsigned long codec_port;
-	unsigned long io2_port;
-	unsigned long mpu_port;
-	unsigned long synth_port;
-	unsigned long mixer_port;
+
+	unsigned long codec_io; /* usually 0xb000, size 128 */
+	unsigned long game_io;  /* usually 0xb400, size 8 */
+	unsigned long mpu_io;   /* usually 0xb800, size 4 */
+	unsigned long opl3_io; /* usually 0xbc00, size 8 */
+	unsigned long mixer_io; /* usually 0xc000, size 64 */
 
 	spinlock_t reg_lock;
 
 	struct snd_timer *timer;
-	
+
 	struct snd_pcm *pcm;
-	struct snd_pcm_substream *playback_substream;
-	struct snd_pcm_substream *capture_substream;
-	unsigned int is_playing;
-	unsigned int is_recording;
+	struct snd_azf3328_audio_stream audio_stream[2];
 
 	struct snd_card *card;
 	struct snd_rawmidi *rmidi;
 
-#ifdef SUPPORT_JOYSTICK
+#ifdef SUPPORT_GAMEPORT
 	struct gameport *gameport;
+	int axes[4];
 #endif
 
 	struct pci_dev *pci;
 	int irq;
 
+	/* register 0x6a is write-only, thus need to remember setting.
+	 * If we need to add more registers here, then we might try to fold this
+	 * into some transparent combined shadow register handling with
+	 * CONFIG_PM register storage below, but that's slightly difficult. */
+	u16 shadow_reg_codec_6AH;
+
 #ifdef CONFIG_PM
 	/* register value containers for power management
 	 * Note: not always full I/O range preserved (just like Win driver!) */
-	u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2];
-	u16 saved_regs_io2   [AZF_IO_SIZE_IO2_PM / 2];
-	u16 saved_regs_mpu   [AZF_IO_SIZE_MPU_PM / 2];
-	u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2];
+	u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2];
+	u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2];
+	u16 saved_regs_mpu  [AZF_IO_SIZE_MPU_PM / 2];
+	u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2];
 	u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
 #endif
 };
@@ -252,126 +314,166 @@ static const struct pci_device_id snd_azf3328_ids[] = {
 
 MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
 
+
+static int
+snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
+{
+	u8 prev = inb(reg), new;
+
+	new = (do_set) ? (prev|mask) : (prev & ~mask);
+	/* we need to always write the new value no matter whether it differs
+	 * or not, since some register bits don't indicate their setting */
+	outb(new, reg);
+	if (new != prev)
+		return 1;
+
+	return 0;
+}
+
 static inline void
-snd_azf3328_codec_outb(const struct snd_azf3328 *chip, int reg, u8 value)
+snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
 {
-	outb(value, chip->codec_port + reg);
+	outb(value, chip->codec_io + reg);
 }
 
 static inline u8
-snd_azf3328_codec_inb(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_codec_inb(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inb(chip->codec_port + reg);
+	return inb(chip->codec_io + reg);
 }
 
 static inline void
-snd_azf3328_codec_outw(const struct snd_azf3328 *chip, int reg, u16 value)
+snd_azf3328_codec_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
 {
-	outw(value, chip->codec_port + reg);
+	outw(value, chip->codec_io + reg);
 }
 
 static inline u16
-snd_azf3328_codec_inw(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_codec_inw(const struct snd_azf3328 *chip, unsigned reg)
+{
+	return inw(chip->codec_io + reg);
+}
+
+static inline void
+snd_azf3328_codec_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
+{
+	outl(value, chip->codec_io + reg);
+}
+
+static inline u32
+snd_azf3328_codec_inl(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inw(chip->codec_port + reg);
+	return inl(chip->codec_io + reg);
 }
 
 static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328 *chip, int reg, u32 value)
+snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
 {
-	outl(value, chip->codec_port + reg);
+	outb(value, chip->game_io + reg);
 }
 
 static inline void
-snd_azf3328_io2_outb(const struct snd_azf3328 *chip, int reg, u8 value)
+snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
 {
-	outb(value, chip->io2_port + reg);
+	outw(value, chip->game_io + reg);
 }
 
 static inline u8
-snd_azf3328_io2_inb(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inb(chip->io2_port + reg);
+	return inb(chip->game_io + reg);
+}
+
+static inline u16
+snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
+{
+	return inw(chip->game_io + reg);
 }
 
 static inline void
-snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, int reg, u16 value)
+snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
 {
-	outw(value, chip->mixer_port + reg);
+	outw(value, chip->mixer_io + reg);
 }
 
 static inline u16
-snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inw(chip->mixer_port + reg);
+	return inw(chip->mixer_io + reg);
 }
 
-static void
-snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, int reg, int do_mute)
+#define AZF_MUTE_BIT 0x80
+
+static int
+snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
+			   unsigned reg, int do_mute
+)
 {
-	unsigned long portbase = chip->mixer_port + reg + 1;
-	unsigned char oldval;
+	unsigned long portbase = chip->mixer_io + reg + 1;
+	int updated;
 
 	/* the mute bit is on the *second* (i.e. right) register of a
 	 * left/right channel setting */
-	oldval = inb(portbase);
-	if (do_mute)
-		oldval |= 0x80;
-	else
-		oldval &= ~0x80;
-	outb(oldval, portbase);
+	updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
+
+	/* indicate whether it was muted before */
+	return (do_mute) ? !updated : updated;
 }
 
 static void
-snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg, unsigned char dst_vol_left, unsigned char dst_vol_right, int chan_sel, int delay)
+snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
+					 unsigned reg,
+					 unsigned char dst_vol_left,
+					 unsigned char dst_vol_right,
+					 int chan_sel, int delay
+)
 {
-	unsigned long portbase = chip->mixer_port + reg;
+	unsigned long portbase = chip->mixer_io + reg;
 	unsigned char curr_vol_left = 0, curr_vol_right = 0;
-	int left_done = 0, right_done = 0;
-	
+	int left_change = 0, right_change = 0;
+
 	snd_azf3328_dbgcallenter();
-	if (chan_sel & SET_CHAN_LEFT)
+
+	if (chan_sel & SET_CHAN_LEFT) {
 		curr_vol_left  = inb(portbase + 1);
-	else
-		left_done = 1;
-	if (chan_sel & SET_CHAN_RIGHT)
+
+		/* take care of muting flag contained in left channel */
+		if (curr_vol_left & AZF_MUTE_BIT)
+			dst_vol_left |= AZF_MUTE_BIT;
+		else
+			dst_vol_left &= ~AZF_MUTE_BIT;
+
+		left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
+	}
+
+	if (chan_sel & SET_CHAN_RIGHT) {
 		curr_vol_right = inb(portbase + 0);
-	else
-		right_done = 1;
-	
-	/* take care of muting flag (0x80) contained in left channel */
-	if (curr_vol_left & 0x80)
-		dst_vol_left |= 0x80;
-	else
-		dst_vol_left &= ~0x80;
+
+		right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
+	}
 
 	do {
-		if (!left_done) {
-			if (curr_vol_left > dst_vol_left)
-				curr_vol_left--;
-			else
-			if (curr_vol_left < dst_vol_left)
-				curr_vol_left++;
-			else
-			    left_done = 1;
-			outb(curr_vol_left, portbase + 1);
+		if (left_change) {
+			if (curr_vol_left != dst_vol_left) {
+				curr_vol_left += left_change;
+				outb(curr_vol_left, portbase + 1);
+			} else
+			    left_change = 0;
 		}
-		if (!right_done) {
-			if (curr_vol_right > dst_vol_right)
-				curr_vol_right--;
-			else
-			if (curr_vol_right < dst_vol_right)
-				curr_vol_right++;
-			else
-			    right_done = 1;
+		if (right_change) {
+			if (curr_vol_right != dst_vol_right) {
+				curr_vol_right += right_change;
+
 			/* during volume change, the right channel is crackling
 			 * somewhat more than the left channel, unfortunately.
 			 * This seems to be a hardware issue. */
-			outb(curr_vol_right, portbase + 0);
+				outb(curr_vol_right, portbase + 0);
+			} else
+			    right_change = 0;
 		}
 		if (delay)
 			mdelay(delay);
-	} while ((!left_done) || (!right_done));
+	} while ((left_change) || (right_change));
 	snd_azf3328_dbgcallleave();
 }
 
@@ -379,7 +481,7 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg
  * general mixer element
  */
 struct azf3328_mixer_reg {
-	unsigned int reg;
+	unsigned reg;
 	unsigned int lchan_shift, rchan_shift;
 	unsigned int mask;
 	unsigned int invert: 1;
@@ -544,13 +646,14 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
 		"Mix", "Mic"
 	};
 	static const char * const texts3[] = {
-                "Mic", "CD", "Video", "Aux",
+		"Mic", "CD", "Video", "Aux",
 		"Line", "Mix", "Mix Mono", "Phone"
         };
 	static const char * const texts4[] = {
 		"pre 3D", "post 3D"
         };
 	struct azf3328_mixer_reg reg;
+	const char * const *p = NULL;
 
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
         uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -561,18 +664,20 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
 	if (reg.reg == IDX_MIXER_ADVCTL2) {
 		switch(reg.lchan_shift) {
 		case 8: /* modem out sel */
-			strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]);
+			p = texts1;
 			break;
 		case 9: /* mono sel source */
-			strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]);
+			p = texts2;
 			break;
 		case 15: /* PCM Out Path */
-			strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]);
+			p = texts4;
 			break;
 		}
 	} else
-        	strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]
-);
+	if (reg.reg == IDX_MIXER_REC_SELECT)
+		p = texts3;
+
+	strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
         return 0;
 }
 
@@ -583,7 +688,7 @@ snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
         struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct azf3328_mixer_reg reg;
         unsigned short val;
-        
+
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	val = snd_azf3328_mixer_inw(chip, reg.reg);
 	if (reg.reg == IDX_MIXER_REC_SELECT) {
@@ -605,7 +710,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
         struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct azf3328_mixer_reg reg;
 	unsigned int oreg, nreg, val;
-        
+
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	oreg = snd_azf3328_mixer_inw(chip, reg.reg);
 	val = oreg;
@@ -631,9 +736,11 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
 static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
 	AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
 	AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
-	AZF3328_MIXER_SWITCH("Wave Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
-	AZF3328_MIXER_VOL_STEREO("Wave Playback Volume", IDX_MIXER_WAVEOUT, 0x1f, 1),
-	AZF3328_MIXER_SWITCH("Wave 3D Bypass Playback Switch", IDX_MIXER_ADVCTL2, 7, 1),
+	AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
+	AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
+					IDX_MIXER_WAVEOUT, 0x1f, 1),
+	AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
+					IDX_MIXER_ADVCTL2, 7, 1),
 	AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
 	AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
 	AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
@@ -717,15 +824,16 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
 	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
 
 	/* mute and zero volume channels */
-	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); idx++) {
+	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
 		snd_azf3328_mixer_outw(chip,
 			snd_azf3328_init_values[idx][0],
 			snd_azf3328_init_values[idx][1]);
 	}
-	
+
 	/* add mixer controls */
 	sw = snd_azf3328_mixer_controls;
-	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls); idx++, sw++) {
+	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
+			++idx, ++sw) {
 		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
 			return err;
 	}
@@ -757,9 +865,9 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
 }
 
 static void
-snd_azf3328_setfmt(struct snd_azf3328 *chip,
-			       unsigned int reg,
-			       unsigned int bitrate,
+snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
+			       unsigned reg,
+			       enum azf_freq_t bitrate,
 			       unsigned int format_width,
 			       unsigned int channels
 )
@@ -769,24 +877,25 @@ snd_azf3328_setfmt(struct snd_azf3328 *chip,
 
 	snd_azf3328_dbgcallenter();
 	switch (bitrate) {
-	case  4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
-	case  4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
-	case  5512: val |= SOUNDFORMAT_FREQ_5510; break; /* the AZF3328 names it "5510" for some strange reason */
-	case  6620: val |= SOUNDFORMAT_FREQ_6620; break;
-	case  8000: val |= SOUNDFORMAT_FREQ_8000; break;
-	case  9600: val |= SOUNDFORMAT_FREQ_9600; break;
-	case 11025: val |= SOUNDFORMAT_FREQ_11025; break;
-	case 13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
-	case 16000: val |= SOUNDFORMAT_FREQ_16000; break;
-	case 22050: val |= SOUNDFORMAT_FREQ_22050; break;
-	case 32000: val |= SOUNDFORMAT_FREQ_32000; break;
-	case 44100: val |= SOUNDFORMAT_FREQ_44100; break;
-	case 48000: val |= SOUNDFORMAT_FREQ_48000; break;
-	case 66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
+	case AZF_FREQ_4000:  val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
+	case AZF_FREQ_4800:  val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
+	case AZF_FREQ_5512:
+		/* the AZF3328 names it "5510" for some strange reason */
+			     val |= SOUNDFORMAT_FREQ_5510; break;
+	case AZF_FREQ_6620:  val |= SOUNDFORMAT_FREQ_6620; break;
+	case AZF_FREQ_8000:  val |= SOUNDFORMAT_FREQ_8000; break;
+	case AZF_FREQ_9600:  val |= SOUNDFORMAT_FREQ_9600; break;
+	case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
+	case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
+	case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
+	case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
+	case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
 	default:
 		snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
-		val |= SOUNDFORMAT_FREQ_44100;
-		break;
+		/* fall-through */
+	case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
+	case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
+	case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
 	}
 	/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
 	/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
@@ -805,10 +914,10 @@ snd_azf3328_setfmt(struct snd_azf3328 *chip,
 		val |= SOUNDFORMAT_FLAG_16BIT;
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
-	
+
 	/* set bitrate/format */
 	snd_azf3328_codec_outw(chip, reg, val);
-	
+
 	/* changing the bitrate/format settings switches off the
 	 * audio output with an annoying click in case of 8/16bit format change
 	 * (maybe shutting down DAC/ADC?), thus immediately
@@ -830,31 +939,95 @@ snd_azf3328_setfmt(struct snd_azf3328 *chip,
 	snd_azf3328_dbgcallleave();
 }
 
+static inline void
+snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
+			    unsigned reg
+)
+{
+	/* choose lowest frequency for low power consumption.
+	 * While this will cause louder noise due to rather coarse frequency,
+	 * it should never matter since output should always
+	 * get disabled properly when idle anyway. */
+	snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1);
+}
+
+static void
+snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip,
+					unsigned bitmask,
+					int enable
+)
+{
+	if (enable)
+		chip->shadow_reg_codec_6AH &= ~bitmask;
+	else
+		chip->shadow_reg_codec_6AH |= bitmask;
+	snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n",
+			bitmask, enable, chip->shadow_reg_codec_6AH);
+	snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH);
+}
+
+static inline void
+snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_dbgplay("codec_enable %d\n", enable);
+	/* no idea what exactly is being done here, but I strongly assume it's
+	 * PM related */
+	snd_azf3328_codec_reg_6AH_update(
+		chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
+	);
+}
+
+static void
+snd_azf3328_codec_activity(struct snd_azf3328 *chip,
+				enum snd_azf3328_stream_index stream_type,
+				int enable
+)
+{
+	int need_change = (chip->audio_stream[stream_type].running != enable);
+
+	snd_azf3328_dbgplay(
+		"codec_activity: type %d, enable %d, need_change %d\n",
+				stream_type, enable, need_change
+	);
+	if (need_change) {
+		enum snd_azf3328_stream_index other =
+			(stream_type == AZF_PLAYBACK) ?
+				AZF_CAPTURE : AZF_PLAYBACK;
+		/* small check to prevent shutting down the other party
+		 * in case it's active */
+		if ((enable) || !(chip->audio_stream[other].running))
+			snd_azf3328_codec_enable(chip, enable);
+
+		/* ...and adjust clock, too
+		 * (reduce noise and power consumption) */
+		if (!enable)
+			snd_azf3328_codec_setfmt_lowpower(
+				chip,
+				chip->audio_stream[stream_type].portbase
+					+ IDX_IO_PLAY_SOUNDFORMAT
+			);
+	}
+	chip->audio_stream[stream_type].running = enable;
+}
+
 static void
 snd_azf3328_setdmaa(struct snd_azf3328 *chip,
 				long unsigned int addr,
                                 unsigned int count,
                                 unsigned int size,
-				int do_recording)
+				enum snd_azf3328_stream_index stream_type
+)
 {
-	unsigned long flags, portbase;
-	unsigned int is_running;
-
 	snd_azf3328_dbgcallenter();
-	if (do_recording) {
-		/* access capture registers, i.e. skip playback reg section */
-		portbase = chip->codec_port + 0x20;
-		is_running = chip->is_recording;
-	} else {
-		/* access the playback register section */
-		portbase = chip->codec_port + 0x00;
-		is_running = chip->is_playing;
-	}
+	if (!chip->audio_stream[stream_type].running) {
+		/* AZF3328 uses a two buffer pointer DMA playback approach */
 
-	/* AZF3328 uses a two buffer pointer DMA playback approach */
-	if (!is_running) {
-		unsigned long addr_area2;
-		unsigned long count_areas, count_tmp; /* width 32bit -- overflow!! */
+		unsigned long flags, portbase, addr_area2;
+
+		/* width 32bit (prevent overflow): */
+		unsigned long count_areas, count_tmp;
+
+		portbase = chip->audio_stream[stream_type].portbase;
 		count_areas = size/2;
 		addr_area2 = addr+count_areas;
 		count_areas--; /* max. index */
@@ -884,11 +1057,11 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
 
 	snd_azf3328_dbgcallenter();
 #if 0
-	snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+	snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
 		runtime->rate,
 		snd_pcm_format_width(runtime->format),
 		runtime->channels);
-	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 0);
+	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_PLAYBACK);
 #endif
 	snd_azf3328_dbgcallleave();
 	return 0;
@@ -906,11 +1079,11 @@ snd_azf3328_capture_prepare(struct snd_pcm_substream *substream)
 
 	snd_azf3328_dbgcallenter();
 #if 0
-	snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
+	snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
 		runtime->rate,
 		snd_pcm_format_width(runtime->format),
 		runtime->channels);
-	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 1);
+	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_CAPTURE);
 #endif
 	snd_azf3328_dbgcallleave();
 	return 0;
@@ -923,6 +1096,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int result = 0;
 	unsigned int status1;
+	int previously_muted;
 
 	snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd);
 
@@ -930,20 +1104,23 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 	case SNDRV_PCM_TRIGGER_START:
 		snd_azf3328_dbgplay("START PLAYBACK\n");
 
-		/* mute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+		/* mute WaveOut (avoid clicking during setup) */
+		previously_muted =
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
 
-		snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+		snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
 			runtime->rate,
 			snd_pcm_format_width(runtime->format),
 			runtime->channels);
 
 		spin_lock(&chip->reg_lock);
-		/* stop playback */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+
+		/* stop playback */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
-	    
+
 		/* FIXME: clear interrupts or what??? */
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_IRQTYPE, 0xffff);
 		spin_unlock(&chip->reg_lock);
@@ -951,7 +1128,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 		snd_azf3328_setdmaa(chip, runtime->dma_addr,
 			snd_pcm_lib_period_bytes(substream),
 			snd_pcm_lib_buffer_bytes(substream),
-			0);
+			AZF_PLAYBACK);
 
 		spin_lock(&chip->reg_lock);
 #ifdef WIN9X
@@ -978,30 +1155,35 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 			DMA_SOMETHING_ELSE);
 #endif
 		spin_unlock(&chip->reg_lock);
+		snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 1);
 
 		/* now unmute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+		if (!previously_muted)
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
 
-		chip->is_playing = 1;
 		snd_azf3328_dbgplay("STARTED PLAYBACK\n");
 		break;
 	case SNDRV_PCM_TRIGGER_RESUME:
 		snd_azf3328_dbgplay("RESUME PLAYBACK\n");
 		/* resume playback if we were active */
-		if (chip->is_playing)
+		spin_lock(&chip->reg_lock);
+		if (chip->audio_stream[AZF_PLAYBACK].running)
 			snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
 				snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
+		spin_unlock(&chip->reg_lock);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		snd_azf3328_dbgplay("STOP PLAYBACK\n");
 
-		/* mute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+		/* mute WaveOut (avoid clicking during setup) */
+		previously_muted =
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
 
 		spin_lock(&chip->reg_lock);
-		/* stop playback */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
 
+		/* stop playback */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
 
@@ -1013,10 +1195,12 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 		status1 &= ~DMA_PLAY_SOMETHING1;
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
 		spin_unlock(&chip->reg_lock);
-	    
+		snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
+
 		/* now unmute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
-		chip->is_playing = 0;
+		if (!previously_muted)
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+
 		snd_azf3328_dbgplay("STOPPED PLAYBACK\n");
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -1035,7 +1219,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 		printk(KERN_ERR "FIXME: unknown trigger mode!\n");
                 return -EINVAL;
 	}
-	
+
 	snd_azf3328_dbgcallleave();
 	return result;
 }
@@ -1057,17 +1241,19 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 
 		snd_azf3328_dbgplay("START CAPTURE\n");
 
-		snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
+		snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
 			runtime->rate,
 			snd_pcm_format_width(runtime->format),
 			runtime->channels);
 
 		spin_lock(&chip->reg_lock);
-		/* stop recording */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
+
+		/* stop recording */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-	    
+
 		/* FIXME: clear interrupts or what??? */
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_IRQTYPE, 0xffff);
 		spin_unlock(&chip->reg_lock);
@@ -1075,7 +1261,7 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 		snd_azf3328_setdmaa(chip, runtime->dma_addr,
 			snd_pcm_lib_period_bytes(substream),
 			snd_pcm_lib_buffer_bytes(substream),
-			1);
+			AZF_CAPTURE);
 
 		spin_lock(&chip->reg_lock);
 #ifdef WIN9X
@@ -1102,24 +1288,27 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 			DMA_SOMETHING_ELSE);
 #endif
 		spin_unlock(&chip->reg_lock);
+		snd_azf3328_codec_activity(chip, AZF_CAPTURE, 1);
 
-		chip->is_recording = 1;
 		snd_azf3328_dbgplay("STARTED CAPTURE\n");
 		break;
 	case SNDRV_PCM_TRIGGER_RESUME:
 		snd_azf3328_dbgplay("RESUME CAPTURE\n");
 		/* resume recording if we were active */
-		if (chip->is_recording)
+		spin_lock(&chip->reg_lock);
+		if (chip->audio_stream[AZF_CAPTURE].running)
 			snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
 				snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
+		spin_unlock(&chip->reg_lock);
 		break;
         case SNDRV_PCM_TRIGGER_STOP:
 		snd_azf3328_dbgplay("STOP CAPTURE\n");
 
 		spin_lock(&chip->reg_lock);
-		/* stop recording */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
 
+		/* stop recording */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
 
@@ -1129,8 +1318,8 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 		status1 &= ~DMA_PLAY_SOMETHING1;
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
 		spin_unlock(&chip->reg_lock);
-	    
-		chip->is_recording = 0;
+		snd_azf3328_codec_activity(chip, AZF_CAPTURE, 0);
+
 		snd_azf3328_dbgplay("STOPPED CAPTURE\n");
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -1149,7 +1338,7 @@ snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 		printk(KERN_ERR "FIXME: unknown trigger mode!\n");
                 return -EINVAL;
 	}
-	
+
 	snd_azf3328_dbgcallleave();
 	return result;
 }
@@ -1162,11 +1351,11 @@ snd_azf3328_playback_pointer(struct snd_pcm_substream *substream)
 	snd_pcm_uframes_t frmres;
 
 #ifdef QUERY_HARDWARE
-	bufptr = inl(chip->codec_port+IDX_IO_PLAY_DMA_START_1);
+	bufptr = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_START_1);
 #else
 	bufptr = substream->runtime->dma_addr;
 #endif
-	result = inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS);
+	result = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_CURRPOS);
 
 	/* calculate offset */
 	result -= bufptr;
@@ -1183,11 +1372,11 @@ snd_azf3328_capture_pointer(struct snd_pcm_substream *substream)
 	snd_pcm_uframes_t frmres;
 
 #ifdef QUERY_HARDWARE
-	bufptr = inl(chip->codec_port+IDX_IO_REC_DMA_START_1);
+	bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1);
 #else
 	bufptr = substream->runtime->dma_addr;
 #endif
-	result = inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS);
+	result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS);
 
 	/* calculate offset */
 	result -= bufptr;
@@ -1196,27 +1385,241 @@ snd_azf3328_capture_pointer(struct snd_pcm_substream *substream)
 	return frmres;
 }
 
+/******************************************************************/
+
+#ifdef SUPPORT_GAMEPORT
+static inline void
+snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_io_reg_setb(
+		chip->game_io+IDX_GAME_HWCONFIG,
+		GAME_HWCFG_IRQ_ENABLE,
+		enable
+	);
+}
+
+static inline void
+snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_io_reg_setb(
+		chip->game_io+IDX_GAME_HWCONFIG,
+		GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
+		enable
+	);
+}
+
+static inline void
+snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_codec_reg_6AH_update(
+		chip, IO_6A_SOMETHING2_GAMEPORT, enable
+	);
+}
+
+static inline void
+snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
+{
+	/*
+	 * skeleton handler only
+	 * (we do not want axis reading in interrupt handler - too much load!)
+	 */
+	snd_azf3328_dbggame("gameport irq\n");
+
+	 /* this should ACK the gameport IRQ properly, hopefully. */
+	snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
+}
+
+static int
+snd_azf3328_gameport_open(struct gameport *gameport, int mode)
+{
+	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+	int res;
+
+	snd_azf3328_dbggame("gameport_open, mode %d\n", mode);
+	switch (mode) {
+	case GAMEPORT_MODE_COOKED:
+	case GAMEPORT_MODE_RAW:
+		res = 0;
+		break;
+	default:
+		res = -1;
+		break;
+	}
+
+	snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
+
+	return res;
+}
+
+static void
+snd_azf3328_gameport_close(struct gameport *gameport)
+{
+	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+
+	snd_azf3328_dbggame("gameport_close\n");
+	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
+}
+
+static int
+snd_azf3328_gameport_cooked_read(struct gameport *gameport,
+				 int *axes,
+				 int *buttons
+)
+{
+	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+	int i;
+	u8 val;
+	unsigned long flags;
+
+	snd_assert(chip, return 0);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
+	*buttons = (~(val) >> 4) & 0xf;
+
+	/* ok, this one is a bit dirty: cooked_read is being polled by a timer,
+	 * thus we're atomic and cannot actively wait in here
+	 * (which would be useful for us since it probably would be better
+	 * to trigger a measurement in here, then wait a short amount of
+	 * time until it's finished, then read values of _this_ measurement).
+	 *
+	 * Thus we simply resort to reading values if they're available already
+	 * and trigger the next measurement.
+	 */
+
+	val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
+	if (val & GAME_AXES_SAMPLING_READY) {
+		for (i = 0; i < 4; ++i) {
+			/* configure the axis to read */
+			val = (i << 4) | 0x0f;
+			snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
+
+			chip->axes[i] = snd_azf3328_game_inw(
+						chip, IDX_GAME_AXIS_VALUE
+					);
+		}
+	}
+
+	/* trigger next axes sampling, to be evaluated the next time we
+	 * enter this function */
+
+	/* for some very, very strange reason we cannot enable
+	 * Measurement Ready monitoring for all axes here,
+	 * at least not when only one joystick connected */
+	val = 0x03; /* we're able to monitor axes 1 and 2 only */
+	snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
+
+	snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	for (i = 0; i < 4; i++) {
+		axes[i] = chip->axes[i];
+		if (axes[i] == 0xffff)
+			axes[i] = -1;
+	}
+
+	snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n",
+		axes[0], axes[1], axes[2], axes[3], *buttons
+	);
+
+	return 0;
+}
+
+static int __devinit
+snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
+{
+	struct gameport *gp;
+
+	chip->gameport = gp = gameport_allocate_port();
+	if (!gp) {
+		printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n");
+		return -ENOMEM;
+	}
+
+	gameport_set_name(gp, "AZF3328 Gameport");
+	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+	gameport_set_dev_parent(gp, &chip->pci->dev);
+	gp->io = chip->game_io;
+	gameport_set_port_data(gp, chip);
+
+	gp->open = snd_azf3328_gameport_open;
+	gp->close = snd_azf3328_gameport_close;
+	gp->fuzz = 16; /* seems ok */
+	gp->cooked_read = snd_azf3328_gameport_cooked_read;
+
+	/* DISABLE legacy address: we don't need it! */
+	snd_azf3328_gameport_legacy_address_enable(chip, 0);
+
+	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
+
+	gameport_register_port(chip->gameport);
+
+	return 0;
+}
+
+static void
+snd_azf3328_gameport_free(struct snd_azf3328 *chip)
+{
+	if (chip->gameport) {
+		gameport_unregister_port(chip->gameport);
+		chip->gameport = NULL;
+	}
+	snd_azf3328_gameport_irq_enable(chip, 0);
+}
+#else
+static inline int
+snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
+static inline void
+snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
+static inline void
+snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
+{
+	printk(KERN_WARNING "huh, game port IRQ occurred!?\n");
+}
+#endif /* SUPPORT_GAMEPORT */
+
+/******************************************************************/
+
+static inline void
+snd_azf3328_irq_log_unknown_type(u8 which)
+{
+	snd_azf3328_dbgplay(
+	"azt3328: unknown IRQ type (%x) occurred, please report!\n",
+		which
+	);
+}
+
 static irqreturn_t
 snd_azf3328_interrupt(int irq, void *dev_id)
 {
 	struct snd_azf3328 *chip = dev_id;
 	u8 status, which;
+#if DEBUG_PLAY_REC
 	static unsigned long irq_count;
+#endif
 
 	status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS);
 
         /* fast path out, to ease interrupt sharing */
-	if (!(status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_MPU401|IRQ_TIMER)))
+	if (!(status &
+		(IRQ_PLAYBACK|IRQ_RECORDING|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
+	))
 		return IRQ_NONE; /* must be interrupt for another device */
 
-	snd_azf3328_dbgplay("Interrupt %ld!\nIDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
-		irq_count,
-		snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
-		snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
-		status);
-		
+	snd_azf3328_dbgplay(
+		"irq_count %ld! IDX_IO_PLAY_FLAGS %04x, "
+		"IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
+			irq_count++ /* debug-only */,
+			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
+			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
+			status
+	);
+
 	if (status & IRQ_TIMER) {
-		/* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */
+		/* snd_azf3328_dbgplay("timer %ld\n",
+			snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
+				& TIMER_VALUE_MASK
+		); */
 		if (chip->timer)
 			snd_timer_interrupt(chip->timer, chip->timer->sticks);
 		/* ACK timer */
@@ -1232,15 +1635,20 @@ snd_azf3328_interrupt(int irq, void *dev_id)
 		snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
                	spin_unlock(&chip->reg_lock);
 
-		if (chip->pcm && chip->playback_substream) {
-			snd_pcm_period_elapsed(chip->playback_substream);
+		if (chip->pcm && chip->audio_stream[AZF_PLAYBACK].substream) {
+			snd_pcm_period_elapsed(
+				chip->audio_stream[AZF_PLAYBACK].substream
+			);
 			snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
 				which,
-				inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS));
+				snd_azf3328_codec_inl(
+					chip, IDX_IO_PLAY_DMA_CURRPOS
+				)
+			);
 		} else
-			snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
+			printk(KERN_WARNING "azt3328: irq handler problem!\n");
 		if (which & IRQ_PLAY_SOMETHING)
-			snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n");
+			snd_azf3328_irq_log_unknown_type(which);
 	}
 	if (status & IRQ_RECORDING) {
                 spin_lock(&chip->reg_lock);
@@ -1249,16 +1657,23 @@ snd_azf3328_interrupt(int irq, void *dev_id)
 		snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
 		spin_unlock(&chip->reg_lock);
 
-		if (chip->pcm && chip->capture_substream) {
-			snd_pcm_period_elapsed(chip->capture_substream);
+		if (chip->pcm && chip->audio_stream[AZF_CAPTURE].substream) {
+			snd_pcm_period_elapsed(
+				chip->audio_stream[AZF_CAPTURE].substream
+			);
 			snd_azf3328_dbgplay("REC  period done (#%x), @ %x\n",
 				which,
-				inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS));
+				snd_azf3328_codec_inl(
+					chip, IDX_IO_REC_DMA_CURRPOS
+				)
+			);
 		} else
-			snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
+			printk(KERN_WARNING "azt3328: irq handler problem!\n");
 		if (which & IRQ_REC_SOMETHING)
-			snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n");
+			snd_azf3328_irq_log_unknown_type(which);
 	}
+	if (status & IRQ_GAMEPORT)
+		snd_azf3328_gameport_interrupt(chip);
 	/* MPU401 has less critical IRQ requirements
 	 * than timer and playback/recording, right? */
 	if (status & IRQ_MPU401) {
@@ -1268,7 +1683,6 @@ snd_azf3328_interrupt(int irq, void *dev_id)
 		 * If so, then I don't know how... */
 		snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n");
 	}
-	irq_count++;
 	return IRQ_HANDLED;
 }
 
@@ -1287,8 +1701,8 @@ static const struct snd_pcm_hardware snd_azf3328_playback =
 	.rates =		SNDRV_PCM_RATE_5512 |
 				SNDRV_PCM_RATE_8000_48000 |
 				SNDRV_PCM_RATE_KNOT,
-	.rate_min =		4000,
-	.rate_max =		66200,
+	.rate_min =		AZF_FREQ_4000,
+	.rate_max =		AZF_FREQ_66200,
 	.channels_min =		1,
 	.channels_max =		2,
 	.buffer_bytes_max =	65536,
@@ -1315,8 +1729,8 @@ static const struct snd_pcm_hardware snd_azf3328_capture =
 	.rates =		SNDRV_PCM_RATE_5512 |
 				SNDRV_PCM_RATE_8000_48000 |
 				SNDRV_PCM_RATE_KNOT,
-	.rate_min =		4000,
-	.rate_max =		66200,
+	.rate_min =		AZF_FREQ_4000,
+	.rate_max =		AZF_FREQ_66200,
 	.channels_min =		1,
 	.channels_max =		2,
 	.buffer_bytes_max =	65536,
@@ -1329,10 +1743,24 @@ static const struct snd_pcm_hardware snd_azf3328_capture =
 
 
 static unsigned int snd_azf3328_fixed_rates[] = {
-	4000, 4800, 5512, 6620, 8000, 9600, 11025, 13240, 16000, 22050, 32000,
-	44100, 48000, 66200 };
+	AZF_FREQ_4000,
+	AZF_FREQ_4800,
+	AZF_FREQ_5512,
+	AZF_FREQ_6620,
+	AZF_FREQ_8000,
+	AZF_FREQ_9600,
+	AZF_FREQ_11025,
+	AZF_FREQ_13240,
+	AZF_FREQ_16000,
+	AZF_FREQ_22050,
+	AZF_FREQ_32000,
+	AZF_FREQ_44100,
+	AZF_FREQ_48000,
+	AZF_FREQ_66200
+};
+
 static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
-	.count = ARRAY_SIZE(snd_azf3328_fixed_rates), 
+	.count = ARRAY_SIZE(snd_azf3328_fixed_rates),
 	.list = snd_azf3328_fixed_rates,
 	.mask = 0,
 };
@@ -1346,7 +1774,7 @@ snd_azf3328_playback_open(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	snd_azf3328_dbgcallenter();
-	chip->playback_substream = substream;
+	chip->audio_stream[AZF_PLAYBACK].substream = substream;
 	runtime->hw = snd_azf3328_playback;
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &snd_azf3328_hw_constraints_rates);
@@ -1361,7 +1789,7 @@ snd_azf3328_capture_open(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	snd_azf3328_dbgcallenter();
-	chip->capture_substream = substream;
+	chip->audio_stream[AZF_CAPTURE].substream = substream;
 	runtime->hw = snd_azf3328_capture;
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &snd_azf3328_hw_constraints_rates);
@@ -1375,7 +1803,7 @@ snd_azf3328_playback_close(struct snd_pcm_substream *substream)
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 
 	snd_azf3328_dbgcallenter();
-	chip->playback_substream = NULL;
+	chip->audio_stream[AZF_PLAYBACK].substream = NULL;
 	snd_azf3328_dbgcallleave();
 	return 0;
 }
@@ -1386,7 +1814,7 @@ snd_azf3328_capture_close(struct snd_pcm_substream *substream)
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 
 	snd_azf3328_dbgcallenter();
-	chip->capture_substream = NULL;
+	chip->audio_stream[AZF_CAPTURE].substream = NULL;
 	snd_azf3328_dbgcallleave();
 	return 0;
 }
@@ -1441,102 +1869,8 @@ snd_azf3328_pcm(struct snd_azf3328 *chip, int device)
 
 /******************************************************************/
 
-#ifdef SUPPORT_JOYSTICK
-static int __devinit
-snd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev)
-{
-	struct gameport *gp;
-	struct resource *r;
-
-	if (!joystick[dev])
-		return -ENODEV;
-
-	if (!(r = request_region(0x200, 8, "AZF3328 gameport"))) {
-		printk(KERN_WARNING "azt3328: cannot reserve joystick ports\n");
-		return -EBUSY;
-	}
-
-	chip->gameport = gp = gameport_allocate_port();
-	if (!gp) {
-		printk(KERN_ERR "azt3328: cannot allocate memory for gameport\n");
-		release_and_free_resource(r);
-		return -ENOMEM;
-	}
-
-	gameport_set_name(gp, "AZF3328 Gameport");
-	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
-	gameport_set_dev_parent(gp, &chip->pci->dev);
-	gp->io = 0x200;
-	gameport_set_port_data(gp, r);
-
-	snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
-			      snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) | LEGACY_JOY);
-
-	gameport_register_port(chip->gameport);
-
-	return 0;
-}
-
-static void
-snd_azf3328_free_joystick(struct snd_azf3328 *chip)
-{
-	if (chip->gameport) {
-		struct resource *r = gameport_get_port_data(chip->gameport);
-
-		gameport_unregister_port(chip->gameport);
-		chip->gameport = NULL;
-		/* disable gameport */
-		snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
-				      snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
-		release_and_free_resource(r);
-	}
-}
-#else
-static inline int
-snd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
-static inline void
-snd_azf3328_free_joystick(struct snd_azf3328 *chip) { }
-#endif
-
-/******************************************************************/
-
-static int
-snd_azf3328_free(struct snd_azf3328 *chip)
-{
-        if (chip->irq < 0)
-                goto __end_hw;
-
-	/* reset (close) mixer */
-	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); /* first mute master volume */
-	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
-
-        /* interrupt setup - mask everything (FIXME!) */
-	/* well, at least we know how to disable the timer IRQ */
-	snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00);
-
-	if (chip->irq >= 0)
-        	synchronize_irq(chip->irq);
-__end_hw:
-	snd_azf3328_free_joystick(chip);
-        if (chip->irq >= 0)
-		free_irq(chip->irq, chip);
-	pci_release_regions(chip->pci);
-	pci_disable_device(chip->pci);
-
-        kfree(chip);
-        return 0;
-}
-
-static int
-snd_azf3328_dev_free(struct snd_device *device)
-{
-	struct snd_azf3328 *chip = device->device_data;
-	return snd_azf3328_free(chip);
-}
-
-/******************************************************************/
-
-/*** NOTE: the physical timer resolution actually is 1024000 ticks per second,
+/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
+ *** (probably derived from main crystal via a divider of 24),
  *** but announcing those attributes to user-space would make programs
  *** configure the timer to a 1 tick value, resulting in an absolutely fatal
  *** timer IRQ storm.
@@ -1564,7 +1898,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
 		delay = 49; /* minimum time is 49 ticks */
 	}
 	snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
-	delay |= TIMER_ENABLE_COUNTDOWN | TIMER_ENABLE_IRQ;
+	delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -1582,7 +1916,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
 	chip = snd_timer_chip(timer);
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* disable timer countdown and interrupt */
-	/* FIXME: should we write TIMER_ACK_IRQ here? */
+	/* FIXME: should we write TIMER_IRQ_ACK here? */
 	snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	snd_azf3328_dbgcallleave();
@@ -1626,9 +1960,10 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
 
 	snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
 	snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
-	if ((err = snd_timer_new(chip->card, "AZF3328", &tid, &timer)) < 0) {
+
+	err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
+	if (err < 0)
 		goto out;
-	}
 
 	strcpy(timer->name, "AZF3328 timer");
 	timer->private_data = chip;
@@ -1636,6 +1971,8 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
 
 	chip->timer = timer;
 
+	snd_azf3328_timer_stop(timer);
+
 	err = 0;
 
 out:
@@ -1645,10 +1982,44 @@ out:
 
 /******************************************************************/
 
+static int
+snd_azf3328_free(struct snd_azf3328 *chip)
+{
+	if (chip->irq < 0)
+		goto __end_hw;
+
+	/* reset (close) mixer:
+	 * first mute master volume, then reset
+	 */
+	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
+	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+
+	snd_azf3328_timer_stop(chip->timer);
+	snd_azf3328_gameport_free(chip);
+
+	if (chip->irq >= 0)
+		synchronize_irq(chip->irq);
+__end_hw:
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+
+	kfree(chip);
+	return 0;
+}
+
+static int
+snd_azf3328_dev_free(struct snd_device *device)
+{
+	struct snd_azf3328 *chip = device->device_data;
+	return snd_azf3328_free(chip);
+}
+
 #if 0
 /* check whether a bit can be modified */
 static void
-snd_azf3328_test_bit(unsigned int reg, int bit)
+snd_azf3328_test_bit(unsigned unsigned reg, int bit)
 {
 	unsigned char val, valoff, valon;
 
@@ -1659,42 +2030,74 @@ snd_azf3328_test_bit(unsigned int reg, int bit)
 
 	outb(val|(1 << bit), reg);
 	valon = inb(reg);
-	
+
 	outb(val, reg);
 
-	printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n", reg, bit, val, valoff, valon);
+	printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n",
+				reg, bit, val, valoff, valon
+	);
 }
 #endif
 
-#if DEBUG_MISC
-static void
+static inline void
 snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
 {
+#if DEBUG_MISC
 	u16 tmp;
 
-	snd_azf3328_dbgmisc("codec_port 0x%lx, io2_port 0x%lx, mpu_port 0x%lx, synth_port 0x%lx, mixer_port 0x%lx, irq %d\n", chip->codec_port, chip->io2_port, chip->mpu_port, chip->synth_port, chip->mixer_port, chip->irq);
-
-	snd_azf3328_dbgmisc("io2 %02x %02x %02x %02x %02x %02x\n", snd_azf3328_io2_inb(chip, 0), snd_azf3328_io2_inb(chip, 1), snd_azf3328_io2_inb(chip, 2), snd_azf3328_io2_inb(chip, 3), snd_azf3328_io2_inb(chip, 4), snd_azf3328_io2_inb(chip, 5));
-
-	for (tmp=0; tmp <= 0x01; tmp += 1)
-		snd_azf3328_dbgmisc("0x%02x: opl 0x%04x, mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, mpu330 0x%04x\n", tmp, inb(0x388 + tmp), inb(0x300 + tmp), inb(0x310 + tmp), inb(0x320 + tmp), inb(0x330 + tmp));
+	snd_azf3328_dbgmisc(
+		"codec_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
+		"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
+		chip->codec_io, chip->game_io, chip->mpu_io,
+		chip->opl3_io, chip->mixer_io, chip->irq
+	);
+
+	snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
+		snd_azf3328_game_inb(chip, 0),
+		snd_azf3328_game_inb(chip, 1),
+		snd_azf3328_game_inb(chip, 2),
+		snd_azf3328_game_inb(chip, 3),
+		snd_azf3328_game_inb(chip, 4),
+		snd_azf3328_game_inb(chip, 5)
+	);
+
+	for (tmp = 0; tmp < 0x07; tmp += 1)
+		snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
+
+	for (tmp = 0; tmp <= 0x07; tmp += 1)
+		snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n",
+			tmp, inb(0x200 + tmp), inb(0x208 + tmp));
+
+	for (tmp = 0; tmp <= 0x01; tmp += 1)
+		snd_azf3328_dbgmisc(
+			"0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
+			"mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
+				tmp,
+				inb(0x300 + tmp),
+				inb(0x310 + tmp),
+				inb(0x320 + tmp),
+				inb(0x330 + tmp),
+				inb(0x388 + tmp),
+				inb(0x38c + tmp)
+		);
 
 	for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2)
-		snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n", tmp, snd_azf3328_codec_inw(chip, tmp));
+		snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n",
+			tmp, snd_azf3328_codec_inw(chip, tmp)
+		);
 
 	for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
-		snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n", tmp, snd_azf3328_mixer_inw(chip, tmp));
+		snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n",
+			tmp, snd_azf3328_mixer_inw(chip, tmp)
+		);
+#endif /* DEBUG_MISC */
 }
-#else
-static inline void
-snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) {}
-#endif
 
 static int __devinit
 snd_azf3328_create(struct snd_card *card,
-                                         struct pci_dev *pci,
-                                         unsigned long device_type,
-                                         struct snd_azf3328 ** rchip)
+		   struct pci_dev *pci,
+		   unsigned long device_type,
+		   struct snd_azf3328 **rchip)
 {
 	struct snd_azf3328 *chip;
 	int err;
@@ -1705,7 +2108,8 @@ snd_azf3328_create(struct snd_card *card,
 
 	*rchip = NULL;
 
-	if ((err = pci_enable_device(pci)) < 0)
+	err = pci_enable_device(pci);
+	if (err < 0)
 		return err;
 
 	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
@@ -1721,20 +2125,25 @@ snd_azf3328_create(struct snd_card *card,
 	/* check if we can restrict PCI DMA transfers to 24 bits */
 	if (pci_set_dma_mask(pci, DMA_24BIT_MASK) < 0 ||
 	    pci_set_consistent_dma_mask(pci, DMA_24BIT_MASK) < 0) {
-		snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
+		snd_printk(KERN_ERR "architecture does not support "
+					"24bit PCI busmaster DMA\n"
+		);
 		err = -ENXIO;
 		goto out_err;
 	}
 
-	if ((err = pci_request_regions(pci, "Aztech AZF3328")) < 0) {
+	err = pci_request_regions(pci, "Aztech AZF3328");
+	if (err < 0)
 		goto out_err;
-	}
 
-	chip->codec_port = pci_resource_start(pci, 0);
-	chip->io2_port   = pci_resource_start(pci, 1);
-	chip->mpu_port   = pci_resource_start(pci, 2);
-	chip->synth_port = pci_resource_start(pci, 3);
-	chip->mixer_port = pci_resource_start(pci, 4);
+	chip->codec_io = pci_resource_start(pci, 0);
+	chip->game_io  = pci_resource_start(pci, 1);
+	chip->mpu_io   = pci_resource_start(pci, 2);
+	chip->opl3_io = pci_resource_start(pci, 3);
+	chip->mixer_io = pci_resource_start(pci, 4);
+
+	chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00;
+	chip->audio_stream[AZF_CAPTURE].portbase   = chip->codec_io + 0x20;
 
 	if (request_irq(pci->irq, snd_azf3328_interrupt,
 			IRQF_SHARED, card->shortname, chip)) {
@@ -1747,29 +2156,29 @@ snd_azf3328_create(struct snd_card *card,
 	synchronize_irq(chip->irq);
 
 	snd_azf3328_debug_show_ports(chip);
-	
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0)
 		goto out_err;
-	}
 
 	/* create mixer interface & switches */
-	if ((err = snd_azf3328_mixer_new(chip)) < 0)
+	err = snd_azf3328_mixer_new(chip);
+	if (err < 0)
 		goto out_err;
 
-#if 0
-	/* set very low bitrate to reduce noise and power consumption? */
-	snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, 5512, 8, 1);
-#endif
+	/* shutdown codecs to save power */
+		/* have snd_azf3328_codec_activity() act properly */
+	chip->audio_stream[AZF_PLAYBACK].running = 1;
+	snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
 
 	/* standard chip init stuff */
-	/* default IRQ init value */
+		/* default IRQ init value */
 	tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
 
 	spin_lock_irq(&chip->reg_lock);
 	snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp);
 	snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp);
 	snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp);
-	snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00); /* disable timer */
 	spin_unlock_irq(&chip->reg_lock);
 
 	snd_card_set_dev(card, &pci->dev);
@@ -1805,52 +2214,61 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 		return -ENOENT;
 	}
 
-	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0 );
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
 	if (card == NULL)
 		return -ENOMEM;
 
 	strcpy(card->driver, "AZF3328");
 	strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
 
-        if ((err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip)) < 0) {
+	err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
+	if (err < 0)
 		goto out_err;
-	}
 
 	card->private_data = chip;
 
-	if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401,
-				        chip->mpu_port, MPU401_INFO_INTEGRATED,
-					pci->irq, 0, &chip->rmidi)) < 0) {
-		snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
+	err = snd_mpu401_uart_new(
+		card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED,
+		pci->irq, 0, &chip->rmidi
+	);
+	if (err < 0) {
+		snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
+				chip->mpu_io
+		);
 		goto out_err;
 	}
 
-	if ((err = snd_azf3328_timer(chip, 0)) < 0) {
+	err = snd_azf3328_timer(chip, 0);
+	if (err < 0)
 		goto out_err;
-	}
 
-	if ((err = snd_azf3328_pcm(chip, 0)) < 0) {
+	err = snd_azf3328_pcm(chip, 0);
+	if (err < 0)
 		goto out_err;
-	}
 
-	if (snd_opl3_create(card, chip->synth_port, chip->synth_port+2,
+	if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
 			    OPL3_HW_AUTO, 1, &opl3) < 0) {
 		snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
-			   chip->synth_port, chip->synth_port+2 );
+			   chip->opl3_io, chip->opl3_io+2
+		);
 	} else {
-		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+		/* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
+		err = snd_opl3_timer_new(opl3, 1, 2);
+		if (err < 0)
+			goto out_err;
+		err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+		if (err < 0)
 			goto out_err;
-		}
 	}
 
 	opl3->private_data = chip;
 
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
-		card->shortname, chip->codec_port, chip->irq);
+		card->shortname, chip->codec_io, chip->irq);
 
-	if ((err = snd_card_register(card)) < 0) {
+	err = snd_card_register(card);
+	if (err < 0)
 		goto out_err;
-	}
 
 #ifdef MODULE
 	printk(
@@ -1861,19 +2279,18 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 	1024000 / seqtimer_scaling, seqtimer_scaling);
 #endif
 
-	if (snd_azf3328_config_joystick(chip, dev) < 0)
-		snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
-			      snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
+	snd_azf3328_gameport(chip, dev);
 
 	pci_set_drvdata(pci, card);
 	dev++;
 
 	err = 0;
 	goto out;
-	
+
 out_err:
+	snd_printk(KERN_ERR "azf3328: something failed, exiting\n");
 	snd_card_free(card);
-	
+
 out:
 	snd_azf3328_dbgcallleave();
 	return err;
@@ -1894,27 +2311,31 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_azf3328 *chip = card->private_data;
-	int reg;
+	unsigned reg;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-	
+
 	snd_pcm_suspend_all(chip->pcm);
 
-	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
-		chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
+		chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2);
 
 	/* make sure to disable master volume etc. to prevent looping sound */
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
-	
-	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
-		chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
-		chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
-		chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
-		chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2);
+
+	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
+		chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2);
+
+	/* manually store the one currently relevant write-only reg, too */
+	chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH;
+
+	for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
+		chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
+		chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
+		chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2);
 
 	pci_disable_device(pci);
 	pci_save_state(pci);
@@ -1927,7 +2348,7 @@ snd_azf3328_resume(struct pci_dev *pci)
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_azf3328 *chip = card->private_data;
-	int reg;
+	unsigned reg;
 
 	pci_set_power_state(pci, PCI_D0);
 	pci_restore_state(pci);
@@ -1939,23 +2360,21 @@ snd_azf3328_resume(struct pci_dev *pci)
 	}
 	pci_set_master(pci);
 
-	for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
-		outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
-		outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
-		outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
-		outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
-		outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
+		outw(chip->saved_regs_game[reg], chip->game_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
+		outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
+		outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
+		outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
+		outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2);
 
 	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/azt3328.h b/sound/pci/azt3328.h
index 679fa992e2bc4db09f5e8a1066e55620295f5e1b..7e3e8942d0733f01fa3b9d5363878f2f9bf2f665 100644
--- a/sound/pci/azt3328.h
+++ b/sound/pci/azt3328.h
@@ -1,7 +1,8 @@
 #ifndef __SOUND_AZT3328_H
 #define __SOUND_AZT3328_H
 
-/* "PU" == "power-up value", as tested on PCI168 PCI rev. 10 */
+/* "PU" == "power-up value", as tested on PCI168 PCI rev. 10
+ * "WRITE_ONLY"  == register does not indicate actual bit values */
 
 /*** main I/O area port indices ***/
 /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
@@ -54,7 +55,10 @@
   #define SOUNDFORMAT_XTAL1		0x00
   #define SOUNDFORMAT_XTAL2		0x01
     /* all _SUSPECTED_ values are not used by Windows drivers, so we don't
-     * have any hard facts, only rough measurements */
+     * have any hard facts, only rough measurements.
+     * All we know is that the crystal used on the board has 24.576MHz,
+     * like many soundcards (which results in the frequencies below when
+     * using certain divider values selected by the values below) */
     #define SOUNDFORMAT_FREQ_SUSPECTED_4000	0x0c | SOUNDFORMAT_XTAL1
     #define SOUNDFORMAT_FREQ_SUSPECTED_4800	0x0a | SOUNDFORMAT_XTAL1
     #define SOUNDFORMAT_FREQ_5510		0x0c | SOUNDFORMAT_XTAL2
@@ -72,6 +76,26 @@
   #define SOUNDFORMAT_FLAG_16BIT	0x0010
   #define SOUNDFORMAT_FLAG_2CHANNELS	0x0020
 
+/* define frequency helpers, for maximum value safety */
+enum azf_freq_t {
+#define AZF_FREQ(rate) AZF_FREQ_##rate = rate
+  AZF_FREQ(4000),
+  AZF_FREQ(4800),
+  AZF_FREQ(5512),
+  AZF_FREQ(6620),
+  AZF_FREQ(8000),
+  AZF_FREQ(9600),
+  AZF_FREQ(11025),
+  AZF_FREQ(13240),
+  AZF_FREQ(16000),
+  AZF_FREQ(22050),
+  AZF_FREQ(32000),
+  AZF_FREQ(44100),
+  AZF_FREQ(48000),
+  AZF_FREQ(66200),
+#undef AZF_FREQ
+} AZF_FREQUENCIES;
+
 /** recording area (see also: playback bit flag definitions) **/
 #define IDX_IO_REC_FLAGS	0x20 /* ??, PU:0x0000 */
 #define IDX_IO_REC_IRQTYPE	0x22 /* ??, PU:0x0000 */
@@ -97,40 +121,171 @@
 
 /** DirectX timer, main interrupt area (FIXME: and something else?) **/ 
 #define IDX_IO_TIMER_VALUE	0x60 /* found this timer area by pure luck :-) */
-  #define TIMER_VALUE_MASK		0x000fffffUL /* timer countdown value; triggers IRQ when timer is finished */
-  #define TIMER_ENABLE_COUNTDOWN	0x01000000UL /* activate the timer countdown */
-  #define TIMER_ENABLE_IRQ		0x02000000UL /* trigger timer IRQ on zero transition */
-  #define TIMER_ACK_IRQ			0x04000000UL /* being set in IRQ handler in case port 0x00 (hmm, not port 0x64!?!?) had 0x0020 set upon IRQ handler */
+  /* timer countdown value; triggers IRQ when timer is finished */
+  #define TIMER_VALUE_MASK		0x000fffffUL
+  /* activate timer countdown */
+  #define TIMER_COUNTDOWN_ENABLE	0x01000000UL
+  /* trigger timer IRQ on zero transition */
+  #define TIMER_IRQ_ENABLE		0x02000000UL
+  /* being set in IRQ handler in case port 0x00 (hmm, not port 0x64!?!?)
+   * had 0x0020 set upon IRQ handler */
+  #define TIMER_IRQ_ACK			0x04000000UL
 #define IDX_IO_IRQSTATUS        0x64
-  #define IRQ_PLAYBACK			0x0001
-  #define IRQ_RECORDING			0x0002
-  #define IRQ_MPU401			0x0010
-  #define IRQ_TIMER			0x0020 /* DirectX timer */
-  #define IRQ_UNKNOWN1			0x0040 /* probably unused, or possibly I2S port? or gameport IRQ? */
-  #define IRQ_UNKNOWN2			0x0080 /* probably unused, or possibly I2S port? or gameport IRQ? */
+  /* some IRQ bit in here might also be used to signal a power-management timer
+   * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
+   * Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which
+   * can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */
+
+  #define IRQ_PLAYBACK	0x0001
+  #define IRQ_RECORDING	0x0002
+  #define IRQ_UNKNOWN1	0x0004 /* most probably I2S port */
+  #define IRQ_GAMEPORT	0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
+  #define IRQ_MPU401	0x0010
+  #define IRQ_TIMER	0x0020 /* DirectX timer */
+  #define IRQ_UNKNOWN2	0x0040 /* probably unused, or possibly I2S port? */
+  #define IRQ_UNKNOWN3	0x0080 /* probably unused, or possibly I2S port? */
 #define IDX_IO_66H		0x66    /* writing 0xffff returns 0x0000 */
-#define IDX_IO_SOME_VALUE	0x68	/* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */
-#define IDX_IO_6AH		0x6A	/* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */
-  #define IO_6A_PAUSE_PLAYBACK		0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */
-#define IDX_IO_6CH		0x6C
-#define IDX_IO_6EH		0x6E	/* writing 0xffff returns 0x83fe */
-/* further I/O indices not saved/restored, so probably not used */
+  /* this is set to e.g. 0x3ff or 0x300, and writable;
+   * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
+#define IDX_IO_SOME_VALUE	0x68
+  #define IO_68_RANDOM_TOGGLE1	0x0100	/* toggles randomly */
+  #define IO_68_RANDOM_TOGGLE2	0x0200	/* toggles randomly */
+  /* umm, nope, behaviour of these bits changes depending on what we wrote
+   * to 0x6b!!
+   * And they change upon playback/stop, too:
+   * Writing a value to 0x68 will display this exact value during playback,
+   * too but when stopped it can fall back to a rather different
+   * seemingly random value). Hmm, possibly this is a register which
+   * has a remote shadow which needs proper device supply which only exists
+   * in case playback is active? Or is this driver-induced?
+   */
+
+/* this WORD can be set to have bits 0x0028 activated (FIXME: correct??);
+ * actually inhibits PCM playback!!! maybe power management??: */
+#define IDX_IO_6AH		0x6A /* WRITE_ONLY! */
+  /* bit 5: enabling this will activate permanent counting of bytes 2/3
+   * at gameport I/O (0xb402/3) (equal values each) and cause
+   * gameport legacy I/O at 0x0200 to be _DISABLED_!
+   * Is this Digital Enhanced Game Port Enable??? Or maybe it's Testmode
+   * for Enhanced Digital Gameport (see 4D Wave DX card): */
+  #define IO_6A_SOMETHING1_GAMEPORT	0x0020
+  /* bit 8; sure, this _pauses_ playback (later resumes at same spot!),
+   * but what the heck is this really about??: */
+  #define IO_6A_PAUSE_PLAYBACK_BIT8	0x0100
+  /* bit 9; sure, this _pauses_ playback (later resumes at same spot!),
+   * but what the heck is this really about??: */
+  #define IO_6A_PAUSE_PLAYBACK_BIT9	0x0200
+	/* BIT8 and BIT9 are _NOT_ able to affect OPL3 MIDI playback,
+	 * thus it suggests influence on PCM only!!
+	 * However OTOH there seems to be no bit anywhere around here
+	 * which is able to disable OPL3... */
+  /* bit 10: enabling this actually changes values at legacy gameport
+   * I/O address (0x200); is this enabling of the Digital Enhanced Game Port???
+   * Or maybe this simply switches off the NE558 circuit, since enabling this
+   * still lets us evaluate button states, but not axis states */
+  #define IO_6A_SOMETHING2_GAMEPORT      0x0400
+	/* writing 0x0300: causes quite some crackling during
+	 * PC activity such as switching windows (PCI traffic??
+	 * --> FIFO/timing settings???) */
+	/* writing 0x0100 plus/or 0x0200 inhibits playback */
+	/* since the Windows .INF file has Flag_Enable_JoyStick and
+	 * Flag_Enable_SB_DOS_Emulation directly together, it stands to reason
+	 * that some other bit in this same register might be responsible
+	 * for SB DOS Emulation activation (note that the file did NOT define
+	 * a switch for OPL3!) */
+#define IDX_IO_6CH		0x6C	/* unknown; fully read-writable */
+#define IDX_IO_6EH		0x6E
+	/* writing 0xffff returns 0x83fe (or 0x03fe only).
+	 * writing 0x83 (and only 0x83!!) to 0x6f will cause 0x6c to switch
+	 * from 0000 to ffff. */
 
+/* further I/O indices not saved/restored and not readable after writing,
+ * so probably not used */
 
-/*** I/O 2 area port indices ***/
+
+/*** Gameport area port indices ***/
 /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ 
-#define AZF_IO_SIZE_IO2		0x08
-#define AZF_IO_SIZE_IO2_PM	0x06
+#define AZF_IO_SIZE_GAME		0x08
+#define AZF_IO_SIZE_GAME_PM	0x06
+
+enum {
+	AZF_GAME_LEGACY_IO_PORT = 0x200
+} AZF_GAME_CONFIGS;
+
+#define IDX_GAME_LEGACY_COMPATIBLE	0x00
+	/* in some operation mode, writing anything to this port
+	 * triggers an interrupt:
+	 * yup, that's in case IDX_GAME_01H has one of the
+	 * axis measurement bits enabled
+	 * (and of course one needs to have GAME_HWCFG_IRQ_ENABLE, too) */
+
+#define IDX_GAME_AXES_CONFIG            0x01
+	/* NOTE: layout of this register awfully similar (read: "identical??")
+	 * to AD1815JS.pdf (p.29) */
+
+  /* enables axis 1 (X axis) measurement: */
+  #define GAME_AXES_ENABLE_1		0x01
+  /* enables axis 2 (Y axis) measurement: */
+  #define GAME_AXES_ENABLE_2		0x02
+  /* enables axis 3 (X axis) measurement: */
+  #define GAME_AXES_ENABLE_3		0x04
+  /* enables axis 4 (Y axis) measurement: */
+  #define GAME_AXES_ENABLE_4		0x08
+  /* selects the current axis to read the measured value of
+   * (at IDX_GAME_AXIS_VALUE):
+   * 00 = axis 1, 01 = axis 2, 10 = axis 3, 11 = axis 4: */
+  #define GAME_AXES_READ_MASK		0x30
+  /* enable to have the latch continuously accept ADC values
+   * (and continuously cause interrupts in case interrupts are enabled);
+   * AD1815JS.pdf says it's ~16ms interval there: */
+  #define GAME_AXES_LATCH_ENABLE	0x40
+  /* joystick data (measured axes) ready for reading: */
+  #define GAME_AXES_SAMPLING_READY	0x80
+
+  /* NOTE: other card specs (SiS960 and others!) state that the
+   * game position latches should be frozen when reading and be freed
+   * (== reset?) after reading!!!
+   * Freezing most likely means disabling 0x40 (GAME_AXES_LATCH_ENABLE),
+   *  but how to free the value? */
+  /* An internet search for "gameport latch ADC" should provide some insight
+   * into how to program such a gameport system. */
+
+  /* writing 0xf0 to 01H once reset both counters to 0, in some special mode!?
+   * yup, in case 6AH 0x20 is not enabled
+   * (and 0x40 is sufficient, 0xf0 is not needed) */
+
+#define IDX_GAME_AXIS_VALUE	0x02
+	/* R: value of currently configured axis (word value!);
+	 * W: trigger axis measurement */
+
+#define IDX_GAME_HWCONFIG	0x04
+	/* note: bits 4 to 7 are never set (== 0) when reading!
+	 * --> reserved bits? */
+  /* enables IRQ notification upon axes measurement ready: */
+  #define GAME_HWCFG_IRQ_ENABLE			0x01
+  /* these bits choose a different frequency for the
+   *  internal ADC counter increment.
+   * hmm, seems to be a combo of bits:
+   * 00 --> standard frequency
+   * 10 --> 1/2
+   * 01 --> 1/20
+   * 11 --> 1/200: */
+  #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK	0x06
 
-#define IDX_IO2_LEGACY_ADDR	0x04
-  #define LEGACY_SOMETHING		0x01 /* OPL3?? */
-  #define LEGACY_JOY			0x08
+  /* enable gameport legacy I/O address (0x200)
+   * I was unable to locate any configurability for a different address: */
+  #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE	0x08
 
+/*** MPU401 ***/
 #define AZF_IO_SIZE_MPU		0x04
 #define AZF_IO_SIZE_MPU_PM	0x04
 
-#define AZF_IO_SIZE_SYNTH	0x08
-#define AZF_IO_SIZE_SYNTH_PM	0x06
+/*** OPL3 synth ***/
+#define AZF_IO_SIZE_OPL3	0x08
+#define AZF_IO_SIZE_OPL3_PM	0x06
+/* hmm, given that a standard OPL3 has 4 registers only,
+ * there might be some enhanced functionality lurking at the end
+ * (especially since register 0x04 has a "non-empty" value 0xfe) */
 
 /*** mixer I/O area port indices ***/
 /* (only 0x22 of 0x40 bytes saved/restored by Windows driver)
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index ecbe79b67e437c0d94018e67e1a15f12984a2dbd..2f8b28add27687cfb63815836a72a06e1050eb92 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -249,6 +249,11 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
 	   .name   = "MSI K8N Diamond MB [SB0438]",
 	   .gpio_type = 2,
 	   .i2c_adc = 1 } ,
+	 /* Another MSI K8N Diamond MB, which has apprently a different SSID */
+	 { .serial = 0x10091102,
+	   .name   = "MSI K8N Diamond MB",
+	   .gpio_type = 2,
+	   .i2c_adc = 1 } ,
 	 /* Shuttle XPC SD31P which has an onboard Creative Labs
 	  * Sound Blaster Live! 24-bit EAX
 	  * high-definition 7.1 audio processor".
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 548c9cc81af5f5c05967ac2eae2d6c9d5db51ab9..2f283ea6ad9af4cd9762c0a5bb7fc116e4e79798 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1528,6 +1528,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
 	 .ca0151_chip = 1,
 	 .spk71 = 1,
 	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
 	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */
 	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index fd221209abcb221f6db0dcf323476b8620e40331..f34bbfb705f5f7c870d50012aa17e7c7b57b93ee 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1578,6 +1578,10 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
 		ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
 	else
 		ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
+	if (emu->card_capabilities->invert_shared_spdif)
+		ucontrol->value.integer.value[0] =
+			!ucontrol->value.integer.value[0];
+		
 	return 0;
 }
 
@@ -1586,15 +1590,18 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
 {
 	unsigned long flags;
 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
-	unsigned int reg, val;
+	unsigned int reg, val, sw;
 	int change = 0;
 
+	sw = ucontrol->value.integer.value[0];
+	if (emu->card_capabilities->invert_shared_spdif)
+		sw = !sw;
 	spin_lock_irqsave(&emu->reg_lock, flags);
 	if ( emu->card_capabilities->i2c_adc) {
 		/* Do nothing for Audigy 2 ZS Notebook */
 	} else if (emu->audigy) {
 		reg = inl(emu->port + A_IOCFG);
-		val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
+		val = sw ? A_IOCFG_GPOUT0 : 0;
 		change = (reg & A_IOCFG_GPOUT0) != val;
 		if (change) {
 			reg &= ~A_IOCFG_GPOUT0;
@@ -1603,7 +1610,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
 		}
 	}
 	reg = inl(emu->port + HCFG);
-	val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0;
+	val = sw ? HCFG_GPOUT0 : 0;
 	change |= (reg & HCFG_GPOUT0) != val;
 	if (change) {
 		reg &= ~HCFG_GPOUT0;
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 916c1dbcd53cf919f6a420b86f893a676115302e..7d379f5131fbf61d4ab527a51bac881592c96fff 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -437,43 +437,49 @@ static void get_single_page_range(struct snd_util_memhdr *hdr,
 	*last_page_ret = last_page;
 }
 
+/* release allocated pages */
+static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
+			       int last_page)
+{
+	int page;
+
+	for (page = first_page; page <= last_page; page++) {
+		free_page((unsigned long)emu->page_ptr_table[page]);
+		emu->page_addr_table[page] = 0;
+		emu->page_ptr_table[page] = NULL;
+	}
+}
+
 /*
  * allocate kernel pages
  */
 static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
 {
 	int page, first_page, last_page;
-	struct snd_dma_buffer dmab;
 
 	emu10k1_memblk_init(blk);
 	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
 	/* allocate kernel pages */
 	for (page = first_page; page <= last_page; page++) {
-		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
-					PAGE_SIZE, &dmab) < 0)
-			goto __fail;
-		if (! is_valid_page(emu, dmab.addr)) {
-			snd_dma_free_pages(&dmab);
-			goto __fail;
+		/* first try to allocate from <4GB zone */
+		struct page *p = alloc_page(GFP_KERNEL | GFP_DMA32 |
+					    __GFP_NOWARN);
+		if (!p || (page_to_pfn(p) & ~(emu->dma_mask >> PAGE_SHIFT))) {
+			if (p)
+				__free_page(p);
+			/* try to allocate from <16MB zone */
+			p = alloc_page(GFP_ATOMIC | GFP_DMA |
+				       __GFP_NORETRY | /* no OOM-killer */
+				       __GFP_NOWARN);
+		}
+		if (!p) {
+			__synth_free_pages(emu, first_page, page - 1);
+			return -ENOMEM;
 		}
-		emu->page_addr_table[page] = dmab.addr;
-		emu->page_ptr_table[page] = dmab.area;
+		emu->page_addr_table[page] = page_to_phys(p);
+		emu->page_ptr_table[page] = page_address(p);
 	}
 	return 0;
-
-__fail:
-	/* release allocated pages */
-	last_page = page - 1;
-	for (page = first_page; page <= last_page; page++) {
-		dmab.area = emu->page_ptr_table[page];
-		dmab.addr = emu->page_addr_table[page];
-		dmab.bytes = PAGE_SIZE;
-		snd_dma_free_pages(&dmab);
-		emu->page_addr_table[page] = 0;
-		emu->page_ptr_table[page] = NULL;
-	}
-
-	return -ENOMEM;
 }
 
 /*
@@ -481,23 +487,10 @@ __fail:
  */
 static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
 {
-	int page, first_page, last_page;
-	struct snd_dma_buffer dmab;
+	int first_page, last_page;
 
 	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
-	dmab.dev.type = SNDRV_DMA_TYPE_DEV;
-	dmab.dev.dev = snd_dma_pci_data(emu->pci);
-	for (page = first_page; page <= last_page; page++) {
-		if (emu->page_ptr_table[page] == NULL)
-			continue;
-		dmab.area = emu->page_ptr_table[page];
-		dmab.addr = emu->page_addr_table[page];
-		dmab.bytes = PAGE_SIZE;
-		snd_dma_free_pages(&dmab);
-		emu->page_addr_table[page] = 0;
-		emu->page_ptr_table[page] = NULL;
-	}
-
+	__synth_free_pages(emu, first_page, last_page);
 	return 0;
 }
 
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a6be6e3e8716026b8a8e129f5e7bc9d9ec31b37f..d2e1093f8e972306727255a46ffb7ce7d59ccf2e 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -2335,7 +2335,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
 	if (!tbl)
 		return -1;
 	if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 		char tmp[10];
 		const char *model = NULL;
 		if (models)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index dcd390b2bbaad8b031c8a7fc49ca5aa91f0b6356..efc682888b3173242510a0705accba40b09ad1cd 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -78,7 +78,7 @@ enum {
 #define AC_VERB_GET_BEEP_CONTROL		0x0f0a
 #define AC_VERB_GET_EAPD_BTLENABLE		0x0f0c
 #define AC_VERB_GET_DIGI_CONVERT_1		0x0f0d
-#define AC_VERB_GET_DIGI_CONVERT_2		0x0f0e
+#define AC_VERB_GET_DIGI_CONVERT_2		0x0f0e /* unused */
 #define AC_VERB_GET_VOLUME_KNOB_CONTROL		0x0f0f
 /* f10-f1a: GPIO */
 #define AC_VERB_GET_GPIO_DATA			0x0f15
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 2177d9af533496a4583d943c6f648d241f9ef078..6e18a422d993efcf68a2bf4be92d534f96f30d25 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -88,7 +88,7 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
 
 static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
 {
-#ifndef CONFIG_SND_DEBUG_DETECT
+#ifndef CONFIG_SND_DEBUG_VERBOSE
 	if (!capable(CAP_SYS_RAWIO))
 		return -EACCES;
 #endif
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b3a618eb42cdf5b4a45bc861312b974b7ce00581..16715a68ba5e59c362b5ea3d3db252b4f022523d 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -55,6 +55,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
 static char *model[SNDRV_CARDS];
 static int position_fix[SNDRV_CARDS];
+static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int single_cmd;
 static int enable_msi;
@@ -69,7 +70,9 @@ module_param_array(model, charp, NULL, 0444);
 MODULE_PARM_DESC(model, "Use the given board model.");
 module_param_array(position_fix, int, NULL, 0444);
 MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
-		 "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+		 "(0 = auto, 1 = none, 2 = POSBUF).");
+module_param_array(bdl_pos_adj, int, NULL, 0644);
+MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
 module_param_array(probe_mask, int, NULL, 0444);
 MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
 module_param(single_cmd, bool, 0444);
@@ -197,6 +200,10 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define ATIHDMI_NUM_CAPTURE	0
 #define ATIHDMI_NUM_PLAYBACK	1
 
+/* TERA has 4 playback and 3 capture */
+#define TERA_NUM_CAPTURE	3
+#define TERA_NUM_PLAYBACK	4
+
 /* this number is statically defined for simplicity */
 #define MAX_AZX_DEV		16
 
@@ -259,9 +266,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 /* position fix mode */
 enum {
 	POS_FIX_AUTO,
-	POS_FIX_NONE,
+	POS_FIX_LPIB,
 	POS_FIX_POSBUF,
-	POS_FIX_FIFO,
 };
 
 /* Defines for ATI HD Audio support in SB450 south bridge */
@@ -285,6 +291,7 @@ struct azx_dev {
 	u32 *posbuf;		/* position buffer pointer */
 
 	unsigned int bufsize;	/* size of the play buffer in bytes */
+	unsigned int period_bytes; /* size of the period in bytes */
 	unsigned int frags;	/* number for period in the play buffer */
 	unsigned int fifo_size;	/* FIFO size */
 
@@ -301,11 +308,11 @@ struct azx_dev {
 					 */
 	unsigned char stream_tag;	/* assigned stream */
 	unsigned char index;		/* stream index */
-	/* for sanity check of position buffer */
-	unsigned int period_intr;
 
 	unsigned int opened :1;
 	unsigned int running :1;
+	unsigned int irq_pending :1;
+	unsigned int irq_ignore :1;
 };
 
 /* CORB/RIRB */
@@ -323,6 +330,7 @@ struct azx_rb {
 struct azx {
 	struct snd_card *card;
 	struct pci_dev *pci;
+	int dev_index;
 
 	/* chip type specific */
 	int driver_type;
@@ -366,9 +374,13 @@ struct azx {
 	unsigned int single_cmd :1;
 	unsigned int polling_mode :1;
 	unsigned int msi :1;
+	unsigned int irq_pending_warned :1;
 
 	/* for debugging */
 	unsigned int last_cmd;	/* last issued command (to sync) */
+
+	/* for pending irqs */
+	struct work_struct irq_pending_work;
 };
 
 /* driver types */
@@ -381,6 +393,7 @@ enum {
 	AZX_DRIVER_SIS,
 	AZX_DRIVER_ULI,
 	AZX_DRIVER_NVIDIA,
+	AZX_DRIVER_TERA,
 };
 
 static char *driver_short_names[] __devinitdata = {
@@ -392,6 +405,7 @@ static char *driver_short_names[] __devinitdata = {
 	[AZX_DRIVER_SIS] = "HDA SIS966",
 	[AZX_DRIVER_ULI] = "HDA ULI M5461",
 	[AZX_DRIVER_NVIDIA] = "HDA NVidia",
+	[AZX_DRIVER_TERA] = "HDA Teradici", 
 };
 
 /*
@@ -426,11 +440,6 @@ static char *driver_short_names[] __devinitdata = {
 /* for pcm support */
 #define get_azx_dev(substream) (substream->runtime->private_data)
 
-/* Get the upper 32bit of the given dma_addr_t
- * Compiler should optimize and eliminate the code if dma_addr_t is 32bit
- */
-#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0)
-
 static int azx_acquire_irq(struct azx *chip, int do_disconnect);
 
 /*
@@ -461,7 +470,7 @@ static void azx_init_cmd_io(struct azx *chip)
 	chip->corb.addr = chip->rb.addr;
 	chip->corb.buf = (u32 *)chip->rb.area;
 	azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
-	azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr));
+	azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
 
 	/* set the corb size to 256 entries (ULI requires explicitly) */
 	azx_writeb(chip, CORBSIZE, 0x02);
@@ -476,7 +485,7 @@ static void azx_init_cmd_io(struct azx *chip)
 	chip->rirb.addr = chip->rb.addr + 2048;
 	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
 	azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
-	azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr));
+	azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
 
 	/* set the rirb size to 256 entries (ULI requires explicitly) */
 	azx_writeb(chip, RIRBSIZE, 0x02);
@@ -847,7 +856,7 @@ static void azx_init_chip(struct azx *chip)
 
 	/* program the position buffer */
 	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
-	azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+	azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
 
 	chip->initialized = 1;
 }
@@ -908,6 +917,8 @@ static void azx_init_pci(struct azx *chip)
 }
 
 
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
+
 /*
  * interrupt handler
  */
@@ -930,11 +941,23 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
 		azx_dev = &chip->azx_dev[i];
 		if (status & azx_dev->sd_int_sta_mask) {
 			azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
-			if (azx_dev->substream && azx_dev->running) {
-				azx_dev->period_intr++;
+			if (!azx_dev->substream || !azx_dev->running)
+				continue;
+			/* ignore the first dummy IRQ (due to pos_adj) */
+			if (azx_dev->irq_ignore) {
+				azx_dev->irq_ignore = 0;
+				continue;
+			}
+			/* check whether this IRQ is really acceptable */
+			if (azx_position_ok(chip, azx_dev)) {
+				azx_dev->irq_pending = 0;
 				spin_unlock(&chip->reg_lock);
 				snd_pcm_period_elapsed(azx_dev->substream);
 				spin_lock(&chip->reg_lock);
+			} else {
+				/* bogus IRQ, process it later */
+				azx_dev->irq_pending = 1;
+				schedule_work(&chip->irq_pending_work);
 			}
 		}
 	}
@@ -958,60 +981,108 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
 }
 
 
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct snd_pcm_substream *substream,
+		      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) {
+		dma_addr_t addr;
+		int chunk;
+
+		if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+			return -EINVAL;
+
+		addr = snd_pcm_sgbuf_get_addr(sgbuf, 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;
+		bdl[2] = cpu_to_le32(chunk);
+		/* program the IOC to enable interrupt
+		 * only when the whole fragment is processed
+		 */
+		size -= chunk;
+		bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+		bdl += 4;
+		azx_dev->frags++;
+		ofs += chunk;
+	}
+	*bdlp = bdl;
+	return ofs;
+}
+
 /*
  * set up BDL entries
  */
-static int azx_setup_periods(struct snd_pcm_substream *substream,
+static int azx_setup_periods(struct azx *chip,
+			     struct snd_pcm_substream *substream,
 			     struct azx_dev *azx_dev)
 {
-	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
 	u32 *bdl;
 	int i, ofs, periods, period_bytes;
+	int pos_adj;
 
 	/* reset BDL address */
 	azx_sd_writel(azx_dev, SD_BDLPL, 0);
 	azx_sd_writel(azx_dev, SD_BDLPU, 0);
 
 	period_bytes = snd_pcm_lib_period_bytes(substream);
+	azx_dev->period_bytes = period_bytes;
 	periods = azx_dev->bufsize / period_bytes;
 
 	/* program the initial BDL entries */
 	bdl = (u32 *)azx_dev->bdl.area;
 	ofs = 0;
 	azx_dev->frags = 0;
-	for (i = 0; i < periods; i++) {
-		int size, rest;
-		if (i >= AZX_MAX_BDL_ENTRIES) {
-			snd_printk(KERN_ERR "Too many BDL entries: "
-				   "buffer=%d, period=%d\n",
-				   azx_dev->bufsize, period_bytes);
-			/* reset */
-			azx_sd_writel(azx_dev, SD_BDLPL, 0);
-			azx_sd_writel(azx_dev, SD_BDLPU, 0);
-			return -EINVAL;
+	azx_dev->irq_ignore = 0;
+	pos_adj = bdl_pos_adj[chip->dev_index];
+	if (pos_adj > 0) {
+		struct snd_pcm_runtime *runtime = substream->runtime;
+		pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
+		if (!pos_adj)
+			pos_adj = 1;
+		pos_adj = frames_to_bytes(runtime, pos_adj);
+		if (pos_adj >= period_bytes) {
+			snd_printk(KERN_WARNING "Too big adjustment %d\n",
+				   bdl_pos_adj[chip->dev_index]);
+			pos_adj = 0;
+		} else {
+			ofs = setup_bdle(substream, azx_dev,
+					 &bdl, ofs, pos_adj, 1);
+			if (ofs < 0)
+				goto error;
+			azx_dev->irq_ignore = 1;
 		}
-		rest = period_bytes;
-		do {
-			dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
-			/* program the address field of the BDL entry */
-			bdl[0] = cpu_to_le32((u32)addr);
-			bdl[1] = cpu_to_le32(upper_32bit(addr));
-			/* program the size field of the BDL entry */
-			size = PAGE_SIZE - (ofs % PAGE_SIZE);
-			if (rest < size)
-				size = rest;
-			bdl[2] = cpu_to_le32(size);
-			/* program the IOC to enable interrupt
-			 * only when the whole fragment is processed
-			 */
-			rest -= size;
-			bdl[3] = rest ? 0 : cpu_to_le32(0x01);
-			bdl += 4;
-			azx_dev->frags++;
-			ofs += size;
-		} while (rest > 0);
+	} else
+		pos_adj = 0;
+	for (i = 0; i < periods; i++) {
+		if (i == periods - 1 && pos_adj)
+			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+					 period_bytes - pos_adj, 0);
+		else
+			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+					 period_bytes, 1);
+		if (ofs < 0)
+			goto error;
 	}
 	return 0;
+
+ error:
+	snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
+		   azx_dev->bufsize, period_bytes);
+	/* reset */
+	azx_sd_writel(azx_dev, SD_BDLPL, 0);
+	azx_sd_writel(azx_dev, SD_BDLPU, 0);
+	return -EINVAL;
 }
 
 /*
@@ -1062,7 +1133,7 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
 	/* lower BDL address */
 	azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
 	/* upper BDL address */
-	azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr));
+	azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
 
 	/* enable the position buffer */
 	if (chip->position_fix == POS_FIX_POSBUF ||
@@ -1085,7 +1156,7 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
  */
 
 static unsigned int azx_max_codecs[] __devinitdata = {
-	[AZX_DRIVER_ICH] = 3,
+	[AZX_DRIVER_ICH] = 4,		/* Some ICH9 boards use SD3 */
 	[AZX_DRIVER_SCH] = 3,
 	[AZX_DRIVER_ATI] = 4,
 	[AZX_DRIVER_ATIHDMI] = 4,
@@ -1093,6 +1164,7 @@ static unsigned int azx_max_codecs[] __devinitdata = {
 	[AZX_DRIVER_SIS] = 3,		/* FIXME: correct? */
 	[AZX_DRIVER_ULI] = 3,		/* FIXME: correct? */
 	[AZX_DRIVER_NVIDIA] = 3,	/* FIXME: correct? */
+	[AZX_DRIVER_TERA] = 1,
 };
 
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
@@ -1316,7 +1388,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
 
 	snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
 		    azx_dev->bufsize, azx_dev->format_val);
-	if (azx_setup_periods(substream, azx_dev) < 0)
+	if (azx_setup_periods(chip, substream, azx_dev) < 0)
 		return -EINVAL;
 	azx_setup_controller(chip, azx_dev);
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -1421,35 +1493,113 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	return 0;
 }
 
-static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+static unsigned int azx_get_position(struct azx *chip,
+				     struct azx_dev *azx_dev)
 {
-	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-	struct azx *chip = apcm->chip;
-	struct azx_dev *azx_dev = get_azx_dev(substream);
 	unsigned int pos;
 
 	if (chip->position_fix == POS_FIX_POSBUF ||
 	    chip->position_fix == POS_FIX_AUTO) {
 		/* use the position buffer */
 		pos = le32_to_cpu(*azx_dev->posbuf);
-		if (chip->position_fix == POS_FIX_AUTO &&
-		    azx_dev->period_intr == 1 && !pos) {
-			printk(KERN_WARNING
-			       "hda-intel: Invalid position buffer, "
-			       "using LPIB read method instead.\n");
-			chip->position_fix = POS_FIX_NONE;
-			goto read_lpib;
-		}
 	} else {
-	read_lpib:
 		/* read LPIB */
 		pos = azx_sd_readl(azx_dev, SD_LPIB);
-		if (chip->position_fix == POS_FIX_FIFO)
-			pos += azx_dev->fifo_size;
 	}
 	if (pos >= azx_dev->bufsize)
 		pos = 0;
-	return bytes_to_frames(substream->runtime, pos);
+	return pos;
+}
+
+static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx *chip = apcm->chip;
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	return bytes_to_frames(substream->runtime,
+			       azx_get_position(chip, azx_dev));
+}
+
+/*
+ * Check whether the current DMA position is acceptable for updating
+ * periods.  Returns non-zero if it's OK.
+ *
+ * Many HD-audio controllers appear pretty inaccurate about
+ * the update-IRQ timing.  The IRQ is issued before actually the
+ * data is processed.  So, we need to process it afterwords in a
+ * workqueue.
+ */
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
+{
+	unsigned int pos;
+
+	pos = azx_get_position(chip, azx_dev);
+	if (chip->position_fix == POS_FIX_AUTO) {
+		if (!pos) {
+			printk(KERN_WARNING
+			       "hda-intel: Invalid position buffer, "
+			       "using LPIB read method instead.\n");
+			chip->position_fix = POS_FIX_LPIB;
+			pos = azx_get_position(chip, azx_dev);
+		} else
+			chip->position_fix = POS_FIX_POSBUF;
+	}
+
+	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 */
+}
+
+/*
+ * The work for pending PCM period updates.
+ */
+static void azx_irq_pending_work(struct work_struct *work)
+{
+	struct azx *chip = container_of(work, struct azx, irq_pending_work);
+	int i, pending;
+
+	if (!chip->irq_pending_warned) {
+		printk(KERN_WARNING
+		       "hda-intel: IRQ timing workaround is activated "
+		       "for card #%d. Suggest a bigger bdl_pos_adj.\n",
+		       chip->card->number);
+		chip->irq_pending_warned = 1;
+	}
+
+	for (;;) {
+		pending = 0;
+		spin_lock_irq(&chip->reg_lock);
+		for (i = 0; i < chip->num_streams; i++) {
+			struct azx_dev *azx_dev = &chip->azx_dev[i];
+			if (!azx_dev->irq_pending ||
+			    !azx_dev->substream ||
+			    !azx_dev->running)
+				continue;
+			if (azx_position_ok(chip, azx_dev)) {
+				azx_dev->irq_pending = 0;
+				spin_unlock(&chip->reg_lock);
+				snd_pcm_period_elapsed(azx_dev->substream);
+				spin_lock(&chip->reg_lock);
+			} else
+				pending++;
+		}
+		spin_unlock_irq(&chip->reg_lock);
+		if (!pending)
+			return;
+		cond_resched();
+	}
+}
+
+/* clear irq_pending flags and assure no on-going workq */
+static void azx_clear_irq_pending(struct azx *chip)
+{
+	int i;
+
+	spin_lock_irq(&chip->reg_lock);
+	for (i = 0; i < chip->num_streams; i++)
+		chip->azx_dev[i].irq_pending = 0;
+	spin_unlock_irq(&chip->reg_lock);
+	flush_scheduled_work();
 }
 
 static struct snd_pcm_ops azx_pcm_ops = {
@@ -1676,6 +1826,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
 	int i;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	azx_clear_irq_pending(chip);
 	for (i = 0; i < AZX_MAX_PCMS; i++)
 		snd_pcm_suspend_all(chip->pcm[i]);
 	if (chip->initialized)
@@ -1732,6 +1883,7 @@ static int azx_free(struct azx *chip)
 	int i;
 
 	if (chip->initialized) {
+		azx_clear_irq_pending(chip);
 		for (i = 0; i < chip->num_streams; i++)
 			azx_stream_stop(chip, &chip->azx_dev[i]);
 		azx_stop_chip(chip);
@@ -1770,9 +1922,9 @@ static int azx_dev_free(struct snd_device *device)
  * white/black-listing for position_fix
  */
 static struct snd_pci_quirk position_fix_list[] __devinitdata = {
-	SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE),
-	SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE),
-	SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE),
+	SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
+	SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+	SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
 	{}
 };
 
@@ -1857,12 +2009,25 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
 	chip->irq = -1;
 	chip->driver_type = driver_type;
 	chip->msi = enable_msi;
+	chip->dev_index = dev;
+	INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
 
 	chip->position_fix = check_position_fix(chip, position_fix[dev]);
 	check_probe_mask(chip, dev);
 
 	chip->single_cmd = single_cmd;
 
+	if (bdl_pos_adj[dev] < 0) {
+		switch (chip->driver_type) {
+		case AZX_DRIVER_ICH:
+			bdl_pos_adj[dev] = 1;
+			break;
+		default:
+			bdl_pos_adj[dev] = 32;
+			break;
+		}
+	}
+
 #if BITS_PER_LONG != 64
 	/* Fix up base address on ULI M5461 */
 	if (chip->driver_type == AZX_DRIVER_ULI) {
@@ -2089,6 +2254,7 @@ static struct pci_device_id azx_ids[] = {
 	{ PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
+	{ PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
@@ -2141,6 +2307,8 @@ static struct pci_device_id azx_ids[] = {
 	{ PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
+	/* Teradici */
+	{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 5633f77f8f3b74885f36ff33dec5481771096871..1e5aff5c48d16910074503907775e6d17eac5d66 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -366,8 +366,6 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
 {
 	unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
 						AC_VERB_GET_DIGI_CONVERT_1, 0);
-	unsigned int digi2 = snd_hda_codec_read(codec, nid, 0,
-						AC_VERB_GET_DIGI_CONVERT_2, 0);
 	snd_iprintf(buffer, "  Digital:");
 	if (digi1 & AC_DIG1_ENABLE)
 		snd_iprintf(buffer, " Enabled");
@@ -386,7 +384,8 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
 	if (digi1 & AC_DIG1_LEVEL)
 		snd_iprintf(buffer, " GenLevel");
 	snd_iprintf(buffer, "\n");
-	snd_iprintf(buffer, "  Digital category: 0x%x\n", digi2 & AC_DIG2_CC);
+	snd_iprintf(buffer, "  Digital category: 0x%x\n",
+		    (digi1 >> 8) & AC_DIG2_CC);
 }
 
 static const char *get_pwr_state(u32 state)
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index a99e86d742782ae14d2123c87696f8428bebd54c..e8003d99f0bfe3bb071d761a9a84922dd7d8bd07 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -23,7 +23,6 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
-#include <linux/mutex.h>
 
 #include <sound/core.h>
 #include "hda_codec.h"
@@ -64,7 +63,6 @@ struct ad198x_spec {
 	/* PCM information */
 	struct hda_pcm pcm_rec[3];	/* used in alc_build_pcms() */
 
-	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
 	unsigned int spdif_route;
 
 	/* dynamic controls, init_verbs and input_mux */
@@ -1618,6 +1616,7 @@ static const char *ad1981_models[AD1981_MODELS] = {
 
 static struct snd_pci_quirk ad1981_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
+	SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
 	/* All HP models */
 	SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
@@ -2623,7 +2622,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 {
 	struct ad198x_spec *spec = codec->spec;
 	hda_nid_t nid;
-	int idx, err;
+	int i, idx, err;
 	char name[32];
 
 	if (! pin)
@@ -2631,16 +2630,26 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 
 	idx = ad1988_pin_idx(pin);
 	nid = ad1988_idx_to_dac(codec, idx);
-	/* specify the DAC as the extra output */
-	if (! spec->multiout.hp_nid)
-		spec->multiout.hp_nid = nid;
-	else
-		spec->multiout.extra_out_nid[0] = nid;
-	/* control HP volume/switch on the output mixer amp */
-	sprintf(name, "%s Playback Volume", pfx);
-	if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
-			       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
-		return err;
+	/* check whether the corresponding DAC was already taken */
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		hda_nid_t pin = spec->autocfg.line_out_pins[i];
+		hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
+		if (dac == nid)
+			break;
+	}
+	if (i >= spec->autocfg.line_outs) {
+		/* specify the DAC as the extra output */
+		if (!spec->multiout.hp_nid)
+			spec->multiout.hp_nid = nid;
+		else
+			spec->multiout.extra_out_nid[0] = nid;
+		/* control HP volume/switch on the output mixer amp */
+		sprintf(name, "%s Playback Volume", pfx);
+		err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+		if (err < 0)
+			return err;
+	}
 	nid = ad1988_mixer_nids[idx];
 	sprintf(name, "%s Playback Switch", pfx);
 	if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
@@ -3177,7 +3186,6 @@ static int patch_ad1884(struct hda_codec *codec)
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -3847,7 +3855,6 @@ static int patch_ad1884a(struct hda_codec *codec)
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -4152,7 +4159,6 @@ static int patch_ad1882(struct hda_codec *codec)
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 6;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 36fd852600352244c17e10f6b49c72e284840ea0..7c1eb23f0cec09ae31901058b11222b1ddeca6e4 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -82,7 +82,6 @@ struct conexant_spec {
 	/* PCM information */
 	struct hda_pcm pcm_rec[2];	/* used in build_pcms() */
 
-	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
 	unsigned int spdif_route;
 
 	/* dynamic controls, init_verbs and input_mux */
@@ -687,7 +686,7 @@ static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
 
 static struct hda_verb cxt5045_init_verbs[] = {
 	/* Line in, Mic */
-	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	/* HP, Amp  */
 	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
@@ -907,10 +906,12 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
 	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
 	SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
-	SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE),
+	SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505",
+		      CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
@@ -928,7 +929,6 @@ static int patch_cxt5045(struct hda_codec *codec)
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -963,6 +963,7 @@ static int patch_cxt5045(struct hda_codec *codec)
 		codec->patch_ops.init = cxt5045_init;
 		break;
 	case CXT5045_LAPTOP_MICSENSE:
+		codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
 		spec->input_mux = &cxt5045_capture_source;
 		spec->num_init_verbs = 2;
 		spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
@@ -1007,15 +1008,19 @@ static int patch_cxt5045(struct hda_codec *codec)
 #endif	
 	}
 
-	/*
-	 * Fix max PCM level to 0 dB
-	 * (originall it has 0x2b steps with 0dB offset 0x14)
-	 */
-	snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
-				  (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
-				  (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-				  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-				  (1 << AC_AMPCAP_MUTE_SHIFT));
+	switch (codec->subsystem_id >> 16) {
+	case 0x103c:
+		/* HP laptop has a really bad sound over 0dB on NID 0x17.
+		 * Fix max PCM level to 0 dB
+		 * (originall it has 0x2b steps with 0dB offset 0x14)
+		 */
+		snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+					  (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+					  (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+					  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+					  (1 << AC_AMPCAP_MUTE_SHIFT));
+		break;
+	}
 
 	return 0;
 }
@@ -1477,7 +1482,6 @@ static int patch_cxt5047(struct hda_codec *codec)
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -1736,7 +1740,6 @@ static int patch_cxt5051(struct hda_codec *codec)
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	codec->patch_ops = conexant_patch_ops;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index b0a2a262ece2f0101f8f885c0637ab37fd6e0ae6..2807bc840d26973f695d6be66c55b6bb5ac58717 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -163,6 +163,10 @@ enum {
 	ALC662_LENOVO_101E,
 	ALC662_ASUS_EEEPC_P701,
 	ALC662_ASUS_EEEPC_EP20,
+	ALC663_ASUS_M51VA,
+	ALC663_ASUS_G71V,
+	ALC663_ASUS_H13,
+	ALC663_ASUS_G50V,
 	ALC662_AUTO,
 	ALC662_MODEL_LAST,
 };
@@ -205,6 +209,7 @@ enum {
 	ALC883_MITAC,
 	ALC883_CLEVO_M720,
 	ALC883_FUJITSU_PI2515,
+	ALC883_3ST_6ch_INTEL,
 	ALC883_AUTO,
 	ALC883_MODEL_LAST,
 };
@@ -280,6 +285,10 @@ struct alc_spec {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	struct hda_loopback_check loopback;
 #endif
+
+	/* for PLL fix */
+	hda_nid_t pll_nid;
+	unsigned int pll_coef_idx, pll_coef_bit;
 };
 
 /*
@@ -747,6 +756,38 @@ static struct hda_verb alc_gpio3_init_verbs[] = {
 	{ }
 };
 
+/*
+ * Fix hardware PLL issue
+ * On some codecs, the analog PLL gating control must be off while
+ * the default value is 1.
+ */
+static void alc_fix_pll(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int val;
+
+	if (!spec->pll_nid)
+		return;
+	snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
+			    spec->pll_coef_idx);
+	val = snd_hda_codec_read(codec, spec->pll_nid, 0,
+				 AC_VERB_GET_PROC_COEF, 0);
+	snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
+			    spec->pll_coef_idx);
+	snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF,
+			    val & ~(1 << spec->pll_coef_bit));
+}
+
+static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
+			     unsigned int coef_idx, unsigned int coef_bit)
+{
+	struct alc_spec *spec = codec->spec;
+	spec->pll_nid = nid;
+	spec->pll_coef_idx = coef_idx;
+	spec->pll_coef_bit = coef_bit;
+	alc_fix_pll(codec);
+}
+
 static void alc_sku_automute(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -776,6 +817,24 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
 	alc_sku_automute(codec);
 }
 
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
+{
+	unsigned int tmp;
+
+	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0);
+	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);
+	if ((tmp & 0xf0) == 2)
+		/* alc888S-VC */
+		snd_hda_codec_read(codec, 0x20, 0,
+				   AC_VERB_SET_PROC_COEF, 0x830);
+	 else
+		 /* alc888-VB */
+		 snd_hda_codec_read(codec, 0x20, 0,
+				    AC_VERB_SET_PROC_COEF, 0x3030);
+}
+
 /* 32-bit subsystem ID for BIOS loading in HD Audio codec.
  *	31 ~ 16 :	Manufacture ID
  *	15 ~ 8	:	SKU ID
@@ -851,8 +910,10 @@ do_sku:
 		case 0x10ec0267:
 		case 0x10ec0268:
 		case 0x10ec0269:
+		case 0x10ec0660:
+		case 0x10ec0662:
+		case 0x10ec0663:
 		case 0x10ec0862:
-		case 0x10ec0662:	
 		case 0x10ec0889:
 			snd_hda_codec_write(codec, 0x14, 0,
 					    AC_VERB_SET_EAPD_BTLENABLE, 2);
@@ -877,7 +938,6 @@ do_sku:
 		case 0x10ec0882:
 		case 0x10ec0883:
 		case 0x10ec0885:
-		case 0x10ec0888:
 		case 0x10ec0889:
 			snd_hda_codec_write(codec, 0x20, 0,
 					    AC_VERB_SET_COEF_INDEX, 7);
@@ -889,6 +949,9 @@ do_sku:
 					    AC_VERB_SET_PROC_COEF,
 					    tmp | 0x2010);
 			break;
+		case 0x10ec0888:
+			alc888_coef_init(codec);
+			break;
 		case 0x10ec0267:
 		case 0x10ec0268:
 			snd_hda_codec_write(codec, 0x20, 0,
@@ -2373,6 +2436,8 @@ static int alc_init(struct hda_codec *codec)
 	struct alc_spec *spec = codec->spec;
 	unsigned int i;
 
+	alc_fix_pll(codec);
+
 	for (i = 0; i < spec->num_init_verbs; i++)
 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
 
@@ -3009,6 +3074,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
 	SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
 	SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+	SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU),
 	SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
 	SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
 	SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
@@ -5101,7 +5167,7 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
 	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),
+	SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
@@ -6127,6 +6193,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+	SND_PCI_QUIRK(0x106b, 0x00a0, "Apple iMac 24''", ALC885_IMAC24),
 	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
 	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
@@ -6353,7 +6420,9 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec)
 			continue;
 		vref = PIN_IN;
 		if (1 /*i <= AUTO_PIN_FRONT_MIC*/) {
-			if (snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) &
+			unsigned int pincap;
+			pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+			if ((pincap >> AC_PINCAP_VREF_SHIFT) &
 			    AC_PINCAP_VREF_80)
 				vref = PIN_VREF80;
 		}
@@ -6450,8 +6519,9 @@ static int patch_alc882(struct hda_codec *codec)
 		case 0x106b1000: /* iMac 24 */
 			board_config = ALC885_IMAC24;
 			break;
-		case 0x106b00a1: /* Macbook */
+		case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
 		case 0x106b2c00: /* Macbook Pro rev3 */
+		case 0x106b3600: /* Macbook 3.1 */
 			board_config = ALC885_MBP3;
 			break;
 		default:
@@ -6485,14 +6555,20 @@ static int patch_alc882(struct hda_codec *codec)
 	if (board_config != ALC882_AUTO)
 		setup_preset(spec, &alc882_presets[board_config]);
 
-	spec->stream_name_analog = "ALC882 Analog";
+	if (codec->vendor_id == 0x10ec0885) {
+		spec->stream_name_analog = "ALC885 Analog";
+		spec->stream_name_digital = "ALC885 Digital";
+	} else {
+		spec->stream_name_analog = "ALC882 Analog";
+		spec->stream_name_digital = "ALC882 Digital";
+	}
+
 	spec->stream_analog_playback = &alc882_pcm_analog_playback;
 	spec->stream_analog_capture = &alc882_pcm_analog_capture;
 	/* FIXME: setup DAC5 */
 	/*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
 	spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
 
-	spec->stream_name_digital = "ALC882 Digital";
 	spec->stream_digital_playback = &alc882_pcm_digital_playback;
 	spec->stream_digital_capture = &alc882_pcm_digital_capture;
 
@@ -6569,6 +6645,16 @@ static struct hda_input_mux alc883_capture_source = {
 	},
 };
 
+static struct hda_input_mux alc883_3stack_6ch_intel = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x1 },
+		{ "Front Mic", 0x0 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
 static struct hda_input_mux alc883_lenovo_101e_capture_source = {
 	.num_items = 2,
 	.items = {
@@ -6649,6 +6735,48 @@ static struct hda_channel_mode alc883_3ST_6ch_modes[3] = {
 	{ 6, alc883_3ST_ch6_init },
 };
 
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
+	{ 2, alc883_3ST_ch2_intel_init },
+	{ 4, alc883_3ST_ch4_intel_init },
+	{ 6, alc883_3ST_ch6_intel_init },
+};
+
 /*
  * 6ch mode
  */
@@ -6881,15 +7009,54 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc883_3ST_6ch_intel_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", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+			      HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("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, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
 	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
 	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
@@ -7729,6 +7896,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
 	[ALC883_MITAC]		= "mitac",
 	[ALC883_CLEVO_M720]	= "clevo-m720",
 	[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
+	[ALC883_3ST_6ch_INTEL]	= "3stack-6ch-intel",
 	[ALC883_AUTO]		= "auto",
 };
 
@@ -7786,6 +7954,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	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),
+	SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
+	SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
 	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
 	{}
 };
@@ -7824,6 +7994,18 @@ static struct alc_config_preset alc883_presets[] = {
 		.need_dac_fix = 1,
 		.input_mux = &alc883_capture_source,
 	},
+	[ALC883_3ST_6ch_INTEL] = {
+		.mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_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_intel_modes),
+		.channel_mode = alc883_3ST_6ch_intel_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_3stack_6ch_intel,
+	},
 	[ALC883_6ST_DIG] = {
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
 		.init_verbs = { alc883_init_verbs },
@@ -8145,6 +8327,8 @@ static int patch_alc883(struct hda_codec *codec)
 
 	codec->spec = spec;
 
+	alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+
 	board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
 						  alc883_models,
 						  alc883_cfg_tbl);
@@ -8171,12 +8355,25 @@ static int patch_alc883(struct hda_codec *codec)
 	if (board_config != ALC883_AUTO)
 		setup_preset(spec, &alc883_presets[board_config]);
 
-	spec->stream_name_analog = "ALC883 Analog";
+	switch (codec->vendor_id) {
+	case 0x10ec0888:
+		spec->stream_name_analog = "ALC888 Analog";
+		spec->stream_name_digital = "ALC888 Digital";
+		break;
+	case 0x10ec0889:
+		spec->stream_name_analog = "ALC889 Analog";
+		spec->stream_name_digital = "ALC889 Digital";
+		break;
+	default:
+		spec->stream_name_analog = "ALC883 Analog";
+		spec->stream_name_digital = "ALC883 Digital";
+		break;
+	}
+
 	spec->stream_analog_playback = &alc883_pcm_analog_playback;
 	spec->stream_analog_capture = &alc883_pcm_analog_capture;
 	spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
 
-	spec->stream_name_digital = "ALC883 Digital";
 	spec->stream_digital_playback = &alc883_pcm_digital_playback;
 	spec->stream_digital_capture = &alc883_pcm_digital_capture;
 
@@ -8189,6 +8386,9 @@ static int patch_alc883(struct hda_codec *codec)
 	codec->patch_ops = alc_patch_ops;
 	if (board_config == ALC883_AUTO)
 		spec->init_hook = alc883_auto_init;
+	else if (codec->vendor_id == 0x10ec0888)
+		spec->init_hook = alc888_coef_init;
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc883_loopbacks;
@@ -9522,6 +9722,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
+		      ALC262_SONY_ASSAMD),
 	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),
@@ -9729,6 +9931,8 @@ static int patch_alc262(struct hda_codec *codec)
 	}
 #endif
 
+	alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+
 	board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
 						  alc262_models,
 						  alc262_cfg_tbl);
@@ -10674,12 +10878,18 @@ static int patch_alc268(struct hda_codec *codec)
 	if (board_config != ALC268_AUTO)
 		setup_preset(spec, &alc268_presets[board_config]);
 
-	spec->stream_name_analog = "ALC268 Analog";
+	if (codec->vendor_id == 0x10ec0267) {
+		spec->stream_name_analog = "ALC267 Analog";
+		spec->stream_name_digital = "ALC267 Digital";
+	} else {
+		spec->stream_name_analog = "ALC268 Analog";
+		spec->stream_name_digital = "ALC268 Digital";
+	}
+
 	spec->stream_analog_playback = &alc268_pcm_analog_playback;
 	spec->stream_analog_capture = &alc268_pcm_analog_capture;
 	spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
 
-	spec->stream_name_digital = "ALC268 Digital";
 	spec->stream_digital_playback = &alc268_pcm_digital_playback;
 
 	if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
@@ -11033,6 +11243,8 @@ static int patch_alc269(struct hda_codec *codec)
 
 	codec->spec = spec;
 
+	alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
 	board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
 						  alc269_models,
 						  alc269_cfg_tbl);
@@ -12631,6 +12843,12 @@ static struct hda_verb alc861vd_eapd_verbs[] = {
 	{ }
 };
 
+static struct hda_verb alc660vd_eapd_verbs[] = {
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{ }
+};
+
 static struct hda_verb alc861vd_lenovo_unsol_verbs[] = {
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -12786,6 +13004,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
 	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
+	SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 N200", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
 	{}
 };
@@ -13168,11 +13387,19 @@ static int patch_alc861vd(struct hda_codec *codec)
 	if (board_config != ALC861VD_AUTO)
 		setup_preset(spec, &alc861vd_presets[board_config]);
 
-	spec->stream_name_analog = "ALC861VD Analog";
+	if (codec->vendor_id == 0x10ec0660) {
+		spec->stream_name_analog = "ALC660-VD Analog";
+		spec->stream_name_digital = "ALC660-VD Digital";
+		/* always turn on EAPD */
+		spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs;
+	} else {
+		spec->stream_name_analog = "ALC861VD Analog";
+		spec->stream_name_digital = "ALC861VD Digital";
+	}
+
 	spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
 	spec->stream_analog_capture = &alc861vd_pcm_analog_capture;
 
-	spec->stream_name_digital = "ALC861VD Digital";
 	spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
 	spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
 
@@ -13251,6 +13478,23 @@ static struct hda_input_mux alc662_eeepc_capture_source = {
 	},
 };
 
+static struct hda_input_mux alc663_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+	},
+};
+
+static struct hda_input_mux alc663_m51va_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Ext-Mic", 0x0 },
+		{ "D-Mic", 0x9 },
+	},
+};
+
 #define alc662_mux_enum_info alc_mux_enum_info
 #define alc662_mux_enum_get alc_mux_enum_get
 #define alc662_mux_enum_put alc882_mux_enum_put
@@ -13431,6 +13675,44 @@ static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc663_m51va_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", 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_g71v_mixer[] = {
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x15, 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_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc663_g50v_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", 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_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc662_chmode_mixer[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -13501,6 +13783,11 @@ static struct hda_verb alc662_init_verbs[] = {
 	{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)},
+
+	/* always trun on EAPD */
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+
 	{ }
 };
 
@@ -13571,6 +13858,43 @@ static struct hda_verb alc662_auto_init_verbs[] = {
 	{ }
 };
 
+static struct hda_verb alc663_m51va_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, 0x00},	/* Headphone */
+
+	{0x23, 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_g71v_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 */
+
+	{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 */
+
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
+	{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_g50v_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, 0x00},	/* Headphone */
+
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x21, 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),
@@ -13692,6 +14016,125 @@ static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
 	alc662_eeepc_ep20_automute(codec);
 }
 
+static void alc663_m51va_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, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc663_m51va_mic_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_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    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));
+	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    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));
+}
+
+static void alc663_m51va_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:
+		alc663_m51va_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_m51va_inithook(struct hda_codec *codec)
+{
+	alc663_m51va_speaker_automute(codec);
+	alc663_m51va_mic_automute(codec);
+}
+
+static void alc663_g71v_hp_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, 0x15, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc663_g71v_front_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, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc663_g71v_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_g71v_hp_automute(codec);
+		break;
+	case ALC880_FRONT_EVENT:
+		alc663_g71v_front_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_g71v_inithook(struct hda_codec *codec)
+{
+	alc663_g71v_front_automute(codec);
+	alc663_g71v_hp_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+
+static void alc663_g50v_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_g50v_inithook(struct hda_codec *codec)
+{
+	alc663_m51va_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc662_loopbacks	alc880_loopbacks
 #endif
@@ -13714,14 +14157,24 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
 	[ALC662_LENOVO_101E]	= "lenovo-101e",
 	[ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
 	[ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
+	[ALC663_ASUS_M51VA] = "m51va",
+	[ALC663_ASUS_G71V] = "g71v",
+	[ALC663_ASUS_H13] = "h13",
+	[ALC663_ASUS_G50V] = "g50v",
 	[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(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
+	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),
 	{}
 };
 
@@ -13809,7 +14262,53 @@ static struct alc_config_preset alc662_presets[] = {
 		.unsol_event = alc662_eeepc_ep20_unsol_event,
 		.init_hook = alc662_eeepc_ep20_inithook,
 	},
-
+	[ALC663_ASUS_M51VA] = {
+		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_m51va_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 = &alc663_m51va_capture_source,
+		.unsol_event = alc663_m51va_unsol_event,
+		.init_hook = alc663_m51va_inithook,
+	},
+	[ALC663_ASUS_G71V] = {
+		.mixers = { alc663_g71v_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_g71v_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 = alc663_g71v_unsol_event,
+		.init_hook = alc663_g71v_inithook,
+	},
+	[ALC663_ASUS_H13] = {
+		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_m51va_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 = &alc663_m51va_capture_source,
+		.unsol_event = alc663_m51va_unsol_event,
+		.init_hook = alc663_m51va_inithook,
+	},
+	[ALC663_ASUS_G50V] = {
+		.mixers = { alc663_g50v_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_g50v_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_6ch_modes),
+		.channel_mode = alc662_3ST_6ch_modes,
+		.input_mux = &alc663_capture_source,
+		.unsol_event = alc663_g50v_unsol_event,
+		.init_hook = alc663_g50v_inithook,
+	},
 };
 
 
@@ -14082,6 +14581,8 @@ static int patch_alc662(struct hda_codec *codec)
 
 	codec->spec = spec;
 
+	alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
 	board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
 						  alc662_models,
 			  	                  alc662_cfg_tbl);
@@ -14108,11 +14609,17 @@ static int patch_alc662(struct hda_codec *codec)
 	if (board_config != ALC662_AUTO)
 		setup_preset(spec, &alc662_presets[board_config]);
 
-	spec->stream_name_analog = "ALC662 Analog";
+	if (codec->vendor_id == 0x10ec0663) {
+		spec->stream_name_analog = "ALC663 Analog";
+		spec->stream_name_digital = "ALC663 Digital";
+	} else {
+		spec->stream_name_analog = "ALC662 Analog";
+		spec->stream_name_digital = "ALC662 Digital";
+	}
+
 	spec->stream_analog_playback = &alc662_pcm_analog_playback;
 	spec->stream_analog_capture = &alc662_pcm_analog_capture;
 
-	spec->stream_name_digital = "ALC662 Digital";
 	spec->stream_digital_playback = &alc662_pcm_digital_playback;
 	spec->stream_digital_capture = &alc662_pcm_digital_capture;
 
@@ -14151,6 +14658,7 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
 	  .patch = patch_alc883 },
 	{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
 	  .patch = patch_alc662 },
+	{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
 	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index a4f44a00bae8eb548708a0dd8903cf0bf652bbb3..08cb77f51880a31265c46cf66691715cfbf5dd55 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -636,21 +636,28 @@ static struct hda_verb stac92hd71bxx_core_init[] = {
 	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 };
 
+#define HD_DISABLE_PORTF 3
 static struct hda_verb stac92hd71bxx_analog_core_init[] = {
+	/* start of config #1 */
+
+	/* connect port 0f to audio mixer */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+	/* unmute right and left channels for node 0x0f */
+	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* start of config #2 */
+
 	/* set master volume and direct control */
 	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
 	/* connect headphone jack to dac1 */
 	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	/* connect ports 0d and 0f to audio mixer */
+	/* connect port 0d to audio mixer */
 	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
-	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
 	/* unmute dac0 input in audio mixer */
 	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
-	/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
+	/* unmute right and left channels for nodes 0x0a, 0xd */
 	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{}
 };
 
@@ -818,6 +825,9 @@ static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
 
+	HDA_CODEC_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),
 	{ } /* end */
@@ -1317,13 +1327,13 @@ static unsigned int ref92hd71bxx_pin_configs[10] = {
 	0x90a000f0, 0x01452050,
 };
 
-static unsigned int dell_m4_1_pin_configs[13] = {
+static unsigned int dell_m4_1_pin_configs[10] = {
 	0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
 	0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
 	0x40f000f0, 0x4f0000f0,
 };
 
-static unsigned int dell_m4_2_pin_configs[13] = {
+static unsigned int dell_m4_2_pin_configs[10] = {
 	0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
 	0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
 	0x40f000f0, 0x044413b0,
@@ -1754,12 +1764,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
 		      "unknown Dell", STAC_9205_DELL_M42),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
 		      "Dell Precision", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
-			  "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
 		      "Dell Precision", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
-		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
 		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
@@ -1770,18 +1776,14 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
 		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
 		      "Dell Precision M4300", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
-		      "Dell Precision", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
 		      "unknown Dell", STAC_9205_DELL_M42),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
+		      "Dell Precision", STAC_9205_DELL_M43),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
+		      "Dell Precision", STAC_9205_DELL_M43),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
+		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
 		      "Dell Inspiron", STAC_9205_DELL_M44),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
@@ -3103,13 +3105,16 @@ static int stac92xx_init(struct hda_codec *codec)
 					0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i],
 					0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+		def_conf = get_defcfg_connect(def_conf);
 		/* outputs are only ports capable of power management
 		 * any attempts on powering down a input port cause the
 		 * referenced VREF to act quirky.
 		 */
 		if (pinctl & AC_PINCTL_IN_EN)
 			continue;
-		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED)
+		/* skip any ports that don't have jacks since presence
+ 		 * detection is useless */
+		if (def_conf && def_conf != AC_JACK_PORT_FIXED)
 			continue;
 		enable_pin_detect(codec, spec->pwr_nids[i], event | i);
 		codec->patch_ops.unsol_event(codec, (event | i) << 26);
@@ -3614,6 +3619,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
 
 	codec->spec = spec;
 	spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+	spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
 	spec->pin_nids = stac92hd71bxx_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec,
 							STAC_92HD71BXX_MODELS,
@@ -3642,6 +3648,19 @@ again:
 		spec->mixer = stac92hd71bxx_mixer;
 		spec->init = stac92hd71bxx_core_init;
 		break;
+	case 0x111d7608: /* 5 Port with Analog Mixer */
+		/* no output amps */
+		spec->num_pwrs = 0;
+		spec->mixer = stac92hd71bxx_analog_mixer;
+
+		/* disable VSW */
+		spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
+		stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
+		break;
+	case 0x111d7603: /* 6 Port with Analog Mixer */
+		/* no output amps */
+		spec->num_pwrs = 0;
+		/* fallthru */
 	default:
 		spec->mixer = stac92hd71bxx_analog_mixer;
 		spec->init = stac92hd71bxx_analog_core_init;
@@ -3653,22 +3672,19 @@ again:
 	/* GPIO0 High = EAPD */
 	spec->gpio_mask = 0x01;
 	spec->gpio_dir = 0x01;
-	spec->gpio_mask = 0x01;
 	spec->gpio_data = 0x01;
 
 	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->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);
 
-	spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
-	spec->pwr_nids = stac92hd71bxx_pwr_nids;
-
 	spec->multiout.num_dacs = 1;
 	spec->multiout.hp_nid = 0x11;
 	spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
@@ -4306,10 +4322,11 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
  	{ .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
  	{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
  	{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
+	{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
+	{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
 	{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
-	{ .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx },
 	{ .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
 	{ .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
 	{ .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
diff --git a/sound/pci/ice1712/envy24ht.h b/sound/pci/ice1712/envy24ht.h
index 43b9e3e858be3d7a6297ab70b7bfd382cbf7fb08..a0c5e009bb4a23eaf4c4ddc97148ed34aea25f36 100644
--- a/sound/pci/ice1712/envy24ht.h
+++ b/sound/pci/ice1712/envy24ht.h
@@ -93,9 +93,13 @@ enum {
 #define VT1724_REG_MPU_TXFIFO		0x0a	/*byte ro. number of bytes in TX fifo*/
 #define VT1724_REG_MPU_RXFIFO		0x0b	/*byte ro. number of bytes in RX fifo*/
 
-//are these 2 the wrong way around? they don't seem to be used yet anyway
-#define VT1724_REG_MPU_CTRL		0x0c	/* byte */
-#define VT1724_REG_MPU_DATA		0x0d	/* byte */
+#define VT1724_REG_MPU_DATA		0x0c	/* byte */
+#define VT1724_REG_MPU_CTRL		0x0d	/* byte */
+#define   VT1724_MPU_UART	0x01
+#define   VT1724_MPU_TX_EMPTY	0x02
+#define   VT1724_MPU_TX_FULL	0x04
+#define   VT1724_MPU_RX_EMPTY	0x08
+#define   VT1724_MPU_RX_FULL	0x10
 
 #define VT1724_REG_MPU_FIFO_WM	0x0e	/*byte set the high/low watermarks for RX/TX fifos*/
 #define   VT1724_MPU_RX_FIFO	0x20	//1=rx fifo watermark 0=tx fifo watermark
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 3208901c740e075166f95bc1b8d51406ab88ba56..762fbd7a750710dec0ede3ee426f20cc70668b2f 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -333,6 +333,8 @@ struct snd_ice1712 {
 	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 */
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 67350901772ce986582ef19e0b8ce9dad738f9cb..e596d777d9dde630d12532c55a578937f8d5b2ee 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -32,7 +32,7 @@
 #include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/info.h>
-#include <sound/mpu401.h>
+#include <sound/rawmidi.h>
 #include <sound/initval.h>
 
 #include <sound/asoundef.h>
@@ -223,30 +223,153 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
 }
 
 /*
- * MPU401 accessor
+ * MIDI
  */
-static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu,
-					    unsigned long addr)
+
+static void vt1724_midi_clear_rx(struct snd_ice1712 *ice)
+{
+	unsigned int count;
+
+	for (count = inb(ICEREG1724(ice, MPU_RXFIFO)); count > 0; --count)
+		inb(ICEREG1724(ice, MPU_DATA));
+}
+
+static inline struct snd_rawmidi_substream *
+get_rawmidi_substream(struct snd_ice1712 *ice, unsigned int stream)
 {
-	/* fix status bits to the standard position */
-	/* only RX_EMPTY and TX_FULL are checked */
-	if (addr == MPU401C(mpu))
-		return (inb(addr) & 0x0c) << 4;
+	return list_first_entry(&ice->rmidi[0]->streams[stream].substreams,
+				struct snd_rawmidi_substream, list);
+}
+
+static void vt1724_midi_write(struct snd_ice1712 *ice)
+{
+	struct snd_rawmidi_substream *s;
+	int count, i;
+	u8 buffer[32];
+
+	s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_OUTPUT);
+	count = 31 - inb(ICEREG1724(ice, MPU_TXFIFO));
+	if (count > 0) {
+		count = snd_rawmidi_transmit(s, buffer, count);
+		for (i = 0; i < count; ++i)
+			outb(buffer[i], ICEREG1724(ice, MPU_DATA));
+	}
+}
+
+static void vt1724_midi_read(struct snd_ice1712 *ice)
+{
+	struct snd_rawmidi_substream *s;
+	int count, i;
+	u8 buffer[32];
+
+	s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_INPUT);
+	count = inb(ICEREG1724(ice, MPU_RXFIFO));
+	if (count > 0) {
+		count = min(count, 32);
+		for (i = 0; i < count; ++i)
+			buffer[i] = inb(ICEREG1724(ice, MPU_DATA));
+		snd_rawmidi_receive(s, buffer, count);
+	}
+}
+
+static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
+				   u8 flag, int enable)
+{
+	struct snd_ice1712 *ice = substream->rmidi->private_data;
+	u8 mask;
+
+	spin_lock_irq(&ice->reg_lock);
+	mask = inb(ICEREG1724(ice, IRQMASK));
+	if (enable)
+		mask &= ~flag;
 	else
-		return inb(addr);
+		mask |= flag;
+	outb(mask, ICEREG1724(ice, IRQMASK));
+	spin_unlock_irq(&ice->reg_lock);
 }
 
-static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu,
-				    unsigned char data, unsigned long addr)
+static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
 {
-	if (addr == MPU401C(mpu)) {
-		if (data == MPU401_ENTER_UART)
-			outb(0x01, addr);
-		/* what else? */
-	} else
-		outb(data, addr);
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 1);
+	return 0;
+}
+
+static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
+{
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 0);
+	return 0;
 }
 
+static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
+{
+	struct snd_ice1712 *ice = s->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	if (up) {
+		ice->midi_output = 1;
+		vt1724_midi_write(ice);
+	} else {
+		ice->midi_output = 0;
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
+{
+	struct snd_ice1712 *ice = s->rmidi->private_data;
+	unsigned long timeout;
+
+	/* 32 bytes should be transmitted in less than about 12 ms */
+	timeout = jiffies + msecs_to_jiffies(15);
+	do {
+		if (inb(ICEREG1724(ice, MPU_CTRL)) & VT1724_MPU_TX_EMPTY)
+			break;
+		schedule_timeout_uninterruptible(1);
+	} while (time_after(timeout, jiffies));
+}
+
+static struct snd_rawmidi_ops vt1724_midi_output_ops = {
+	.open = vt1724_midi_output_open,
+	.close = vt1724_midi_output_close,
+	.trigger = vt1724_midi_output_trigger,
+	.drain = vt1724_midi_output_drain,
+};
+
+static int vt1724_midi_input_open(struct snd_rawmidi_substream *s)
+{
+	vt1724_midi_clear_rx(s->rmidi->private_data);
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 1);
+	return 0;
+}
+
+static int vt1724_midi_input_close(struct snd_rawmidi_substream *s)
+{
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 0);
+	return 0;
+}
+
+static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
+{
+	struct snd_ice1712 *ice = s->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	if (up) {
+		ice->midi_input = 1;
+		vt1724_midi_read(ice);
+	} else {
+		ice->midi_input = 0;
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops vt1724_midi_input_ops = {
+	.open = vt1724_midi_input_open,
+	.close = vt1724_midi_input_close,
+	.trigger = vt1724_midi_input_trigger,
+};
+
 
 /*
  *  Interrupt handler
@@ -278,13 +401,10 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
 #endif
 		handled = 1;		
 		if (status & VT1724_IRQ_MPU_TX) {
-			if (ice->rmidi[0])
-				snd_mpu401_uart_interrupt_tx(irq,
-					ice->rmidi[0]->private_data);
-			else /* disable TX to be sure */
-				outb(inb(ICEREG1724(ice, IRQMASK)) |
-				     VT1724_IRQ_MPU_TX,
-				     ICEREG1724(ice, IRQMASK));
+			spin_lock(&ice->reg_lock);
+			if (ice->midi_output)
+				vt1724_midi_write(ice);
+			spin_unlock(&ice->reg_lock);
 			/* Due to mysterical reasons, MPU_TX is always
 			 * generated (and can't be cleared) when a PCM
 			 * playback is going.  So let's ignore at the
@@ -293,13 +413,12 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
 			status_mask &= ~VT1724_IRQ_MPU_TX;
 		}
 		if (status & VT1724_IRQ_MPU_RX) {
-			if (ice->rmidi[0])
-				snd_mpu401_uart_interrupt(irq,
-					ice->rmidi[0]->private_data);
-			else /* disable RX to be sure */
-				outb(inb(ICEREG1724(ice, IRQMASK)) |
-				     VT1724_IRQ_MPU_RX,
-				     ICEREG1724(ice, IRQMASK));
+			spin_lock(&ice->reg_lock);
+			if (ice->midi_input)
+				vt1724_midi_read(ice);
+			else
+				vt1724_midi_clear_rx(ice);
+			spin_unlock(&ice->reg_lock);
 		}
 		/* ack MPU irq */
 		outb(status, ICEREG1724(ice, IRQSTAT));
@@ -2425,28 +2544,30 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
 
 	if (! c->no_mpu401) {
 		if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
-			struct snd_mpu401 *mpu;
-			if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
-						       ICEREG1724(ice, MPU_CTRL),
-						       (MPU401_INFO_INTEGRATED |
-							MPU401_INFO_NO_ACK |
-							MPU401_INFO_TX_IRQ),
-						       ice->irq, 0,
-						       &ice->rmidi[0])) < 0) {
+			struct snd_rawmidi *rmidi;
+
+			err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
+			if (err < 0) {
 				snd_card_free(card);
 				return err;
 			}
-			mpu = ice->rmidi[0]->private_data;
-			mpu->read = snd_vt1724_mpu401_read;
-			mpu->write = snd_vt1724_mpu401_write;
-			/* unmask MPU RX/TX irqs */
-			outb(inb(ICEREG1724(ice, IRQMASK)) &
-			     ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
-			     ICEREG1724(ice, IRQMASK));
+			ice->rmidi[0] = rmidi;
+			rmidi->private_data = ice;
+			strcpy(rmidi->name, "ICE1724 MIDI");
+			rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+					    SNDRV_RAWMIDI_INFO_INPUT |
+					    SNDRV_RAWMIDI_INFO_DUPLEX;
+			snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+					    &vt1724_midi_output_ops);
+			snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+					    &vt1724_midi_input_ops);
+
 			/* set watermarks */
 			outb(VT1724_MPU_RX_FIFO | 0x1,
 			     ICEREG1724(ice, MPU_FIFO_WM));
 			outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
+			/* set UART mode */
+			outb(VT1724_MPU_UART, ICEREG1724(ice, MPU_CTRL));
 		}
 	}
 
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index a536c59fbea1e4faed5155ce579ac0ac800e572b..f4788dee05c36efe80ec6117d66bb469afdeecaa 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2427,6 +2427,29 @@ snd_m3_amp_enable(struct snd_m3 *chip, int enable)
 	outw(0xffff, io + GPIO_MASK);
 }
 
+static void
+snd_m3_hv_init(struct snd_m3 *chip)
+{
+	unsigned long io = chip->iobase;
+	u16 val = GPI_VOL_DOWN | GPI_VOL_UP;
+
+	if (!chip->is_omnibook)
+		return;
+
+	/*
+	 * Volume buttons on some HP OmniBook laptops
+	 * require some GPIO magic to work correctly.
+	 */
+	outw(0xffff, io + GPIO_MASK);
+	outw(0x0000, io + GPIO_DATA);
+
+	outw(~val, io + GPIO_MASK);
+	outw(inw(io + GPIO_DIRECTION) & ~val, io + GPIO_DIRECTION);
+	outw(val, io + GPIO_MASK);
+
+	outw(0xffff, io + GPIO_MASK);
+}
+
 static int
 snd_m3_chip_init(struct snd_m3 *chip)
 {
@@ -2442,21 +2465,6 @@ snd_m3_chip_init(struct snd_m3 *chip)
 	       DISABLE_LEGACY);
 	pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
 
-	if (chip->is_omnibook) {
-		/*
-		 * Volume buttons on some HP OmniBook laptops don't work
-		 * correctly. This makes them work for the most part.
-		 *
-		 * Volume up and down buttons on the laptop side work.
-		 * Fn+cursor_up (volme up) works.
-		 * Fn+cursor_down (volume down) doesn't work.
-		 * Fn+F7 (mute) works acts as volume up.
-		 */
-		outw(~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_MASK);
-		outw(inw(io + GPIO_DIRECTION) & ~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DIRECTION);
-		outw((GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DATA);
-		outw(0xffff, io + GPIO_MASK);
-	}
 	pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
 	n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD);
 	n |= chip->hv_config;
@@ -2642,6 +2650,8 @@ static int m3_resume(struct pci_dev *pci)
 	snd_m3_enable_ints(chip);
 	snd_m3_amp_enable(chip, 1);
 
+	snd_m3_hv_init(chip);
+
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
 }
@@ -2781,6 +2791,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
 
 	snd_m3_amp_enable(chip, 1);
 
+	snd_m3_hv_init(chip);
+
 	tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
 
 	if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 7efb838d18a6b3e78c0b5ceedeccc85887187b2c..06d13e7171148d261fdeeccd21563da3ef59d577 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1302,8 +1302,8 @@ snd_nm256_mixer(struct nm256 *chip)
 		.read = snd_nm256_ac97_read,
 	};
 
-	chip->ac97_regs = kcalloc(sizeof(short),
-				  ARRAY_SIZE(nm256_ac97_init_val), GFP_KERNEL);
+	chip->ac97_regs = kcalloc(ARRAY_SIZE(nm256_ac97_init_val),
+				  sizeof(short), GFP_KERNEL);
 	if (! chip->ac97_regs)
 		return -ENOMEM;
 
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 090dd4354a2838fa2e818265270965c829460c34..7442460583dde0c68f92f40c965afe8cbb461272 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -28,7 +28,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("TempoTec HiFier driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -62,16 +62,28 @@ static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
 			 AK4396_WRITE | (reg << 8) | value);
 }
 
-static void hifier_init(struct oxygen *chip)
+static void update_ak4396_volume(struct oxygen *chip)
+{
+	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
+}
+
+static void hifier_registers_init(struct oxygen *chip)
 {
 	struct hifier_data *data = chip->model_data;
 
-	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
 	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
 	ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
 	ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
-	ak4396_write(chip, AK4396_LCH_ATT, 0);
-	ak4396_write(chip, AK4396_RCH_ATT, 0);
+	update_ak4396_volume(chip);
+}
+
+static void hifier_init(struct oxygen *chip)
+{
+	struct hifier_data *data = chip->model_data;
+
+	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+	hifier_registers_init(chip);
 
 	snd_component_add(chip->card, "AK4396");
 	snd_component_add(chip->card, "CS5340");
@@ -100,12 +112,6 @@ static void set_ak4396_params(struct oxygen *chip,
 	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
 }
 
-static void update_ak4396_volume(struct oxygen *chip)
-{
-	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
-	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
-}
-
 static void update_ak4396_mute(struct oxygen *chip)
 {
 	struct hifier_data *data = chip->model_data;
@@ -140,6 +146,7 @@ static const struct oxygen_model model_hifier = {
 	.init = hifier_init,
 	.control_filter = hifier_control_filter,
 	.cleanup = hifier_cleanup,
+	.resume = hifier_registers_init,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_cs5340_params,
 	.update_dac_volume = update_ak4396_volume,
@@ -180,6 +187,10 @@ static struct pci_driver hifier_driver = {
 	.id_table = hifier_ids,
 	.probe = hifier_probe,
 	.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+	.suspend = oxygen_pci_suspend,
+	.resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_hifier_init(void)
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 63f185c1ed1ed899a0af6786d4092731a87f0c26..7c8ae31eb4685b857488e931a55efcd8f4e1826e 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -43,7 +43,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("C-Media CMI8788 driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -80,6 +80,7 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
 
 struct generic_data {
 	u8 ak4396_ctl2;
+	u16 saved_wm8785_registers[2];
 };
 
 static void ak4396_write(struct oxygen *chip, unsigned int codec,
@@ -99,20 +100,35 @@ static void ak4396_write(struct oxygen *chip, unsigned int codec,
 
 static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
 {
+	struct generic_data *data = chip->model_data;
+
 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 			 OXYGEN_SPI_DATA_LENGTH_2 |
 			 OXYGEN_SPI_CLOCK_160 |
 			 (3 << OXYGEN_SPI_CODEC_SHIFT) |
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
 			 (reg << 9) | value);
+	if (reg < ARRAY_SIZE(data->saved_wm8785_registers))
+		data->saved_wm8785_registers[reg] = value;
 }
 
-static void ak4396_init(struct oxygen *chip)
+static void update_ak4396_volume(struct oxygen *chip)
+{
+	unsigned int i;
+
+	for (i = 0; i < 4; ++i) {
+		ak4396_write(chip, i,
+			     AK4396_LCH_ATT, chip->dac_volume[i * 2]);
+		ak4396_write(chip, i,
+			     AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
+	}
+}
+
+static void ak4396_registers_init(struct oxygen *chip)
 {
 	struct generic_data *data = chip->model_data;
 	unsigned int i;
 
-	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
 	for (i = 0; i < 4; ++i) {
 		ak4396_write(chip, i,
 			     AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
@@ -120,9 +136,16 @@ static void ak4396_init(struct oxygen *chip)
 			     AK4396_CONTROL_2, data->ak4396_ctl2);
 		ak4396_write(chip, i,
 			     AK4396_CONTROL_3, AK4396_PCM);
-		ak4396_write(chip, i, AK4396_LCH_ATT, 0);
-		ak4396_write(chip, i, AK4396_RCH_ATT, 0);
 	}
+	update_ak4396_volume(chip);
+}
+
+static void ak4396_init(struct oxygen *chip)
+{
+	struct generic_data *data = chip->model_data;
+
+	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+	ak4396_registers_init(chip);
 	snd_component_add(chip->card, "AK4396");
 }
 
@@ -133,12 +156,23 @@ static void ak5385_init(struct oxygen *chip)
 	snd_component_add(chip->card, "AK5385");
 }
 
-static void wm8785_init(struct oxygen *chip)
+static void wm8785_registers_init(struct oxygen *chip)
 {
+	struct generic_data *data = chip->model_data;
+
 	wm8785_write(chip, WM8785_R7, 0);
-	wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE |
-		     WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST);
-	wm8785_write(chip, WM8785_R1, WM8785_WL_24);
+	wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]);
+	wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]);
+}
+
+static void wm8785_init(struct oxygen *chip)
+{
+	struct generic_data *data = chip->model_data;
+
+	data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE |
+		WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
+	data->saved_wm8785_registers[1] = WM8785_WL_24;
+	wm8785_registers_init(chip);
 	snd_component_add(chip->card, "WM8785");
 }
 
@@ -158,6 +192,12 @@ static void generic_cleanup(struct oxygen *chip)
 {
 }
 
+static void generic_resume(struct oxygen *chip)
+{
+	ak4396_registers_init(chip);
+	wm8785_registers_init(chip);
+}
+
 static void set_ak4396_params(struct oxygen *chip,
 			      struct snd_pcm_hw_params *params)
 {
@@ -183,18 +223,6 @@ static void set_ak4396_params(struct oxygen *chip,
 	}
 }
 
-static void update_ak4396_volume(struct oxygen *chip)
-{
-	unsigned int i;
-
-	for (i = 0; i < 4; ++i) {
-		ak4396_write(chip, i,
-			     AK4396_LCH_ATT, chip->dac_volume[i * 2]);
-		ak4396_write(chip, i,
-			     AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
-	}
-}
-
 static void update_ak4396_mute(struct oxygen *chip)
 {
 	struct generic_data *data = chip->model_data;
@@ -256,6 +284,7 @@ static const struct oxygen_model model_generic = {
 	.owner = THIS_MODULE,
 	.init = generic_init,
 	.cleanup = generic_cleanup,
+	.resume = generic_resume,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_wm8785_params,
 	.update_dac_volume = update_ak4396_volume,
@@ -283,6 +312,7 @@ static const struct oxygen_model model_meridian = {
 	.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,
@@ -331,6 +361,10 @@ static struct pci_driver oxygen_driver = {
 	.id_table = oxygen_ids,
 	.probe = generic_oxygen_probe,
 	.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+	.suspend = oxygen_pci_suspend,
+	.resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_oxygen_init(void)
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index a71c6e059260543c08b172f4fd71e3bdd0f5eef8..74a6448800747acb0de19ce4eb70db51a4c67f7c 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -16,6 +16,8 @@
 #define PCM_AC97	5
 #define PCM_COUNT	6
 
+#define OXYGEN_IO_SIZE	0x100
+
 /* model-specific configuration of outputs/inputs */
 #define PLAYBACK_0_TO_I2S	0x001
 #define PLAYBACK_1_TO_SPDIF	0x004
@@ -78,6 +80,12 @@ struct oxygen {
 	struct work_struct spdif_input_bits_work;
 	struct work_struct gpio_work;
 	wait_queue_head_t ac97_waitqueue;
+	union {
+		u8 _8[OXYGEN_IO_SIZE];
+		__le16 _16[OXYGEN_IO_SIZE / 2];
+		__le32 _32[OXYGEN_IO_SIZE / 4];
+	} saved_registers;
+	u16 saved_ac97_registers[2][0x40];
 };
 
 struct oxygen_model {
@@ -89,6 +97,8 @@ struct oxygen_model {
 	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,
@@ -117,6 +127,10 @@ struct oxygen_model {
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
 		     const struct oxygen_model *model);
 void oxygen_pci_remove(struct pci_dev *pci);
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
+int oxygen_pci_resume(struct pci_dev *pci);
+#endif
 
 /* oxygen_mixer.c */
 
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 5569606ee87ff22bfb0ac9b9a15e9635e7bd4ed4..83f135f80df44439f62aed9f0b2e3b72d76172a8 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -44,18 +44,21 @@ EXPORT_SYMBOL(oxygen_read32);
 void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
 {
 	outb(value, chip->addr + reg);
+	chip->saved_registers._8[reg] = value;
 }
 EXPORT_SYMBOL(oxygen_write8);
 
 void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
 {
 	outw(value, chip->addr + reg);
+	chip->saved_registers._16[reg / 2] = cpu_to_le16(value);
 }
 EXPORT_SYMBOL(oxygen_write16);
 
 void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
 {
 	outl(value, chip->addr + reg);
+	chip->saved_registers._32[reg / 4] = cpu_to_le32(value);
 }
 EXPORT_SYMBOL(oxygen_write32);
 
@@ -63,7 +66,10 @@ void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
 			  u8 value, u8 mask)
 {
 	u8 tmp = inb(chip->addr + reg);
-	outb((tmp & ~mask) | (value & mask), chip->addr + reg);
+	tmp &= ~mask;
+	tmp |= value & mask;
+	outb(tmp, chip->addr + reg);
+	chip->saved_registers._8[reg] = tmp;
 }
 EXPORT_SYMBOL(oxygen_write8_masked);
 
@@ -71,7 +77,10 @@ void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
 			   u16 value, u16 mask)
 {
 	u16 tmp = inw(chip->addr + reg);
-	outw((tmp & ~mask) | (value & mask), chip->addr + reg);
+	tmp &= ~mask;
+	tmp |= value & mask;
+	outw(tmp, chip->addr + reg);
+	chip->saved_registers._16[reg / 2] = cpu_to_le16(tmp);
 }
 EXPORT_SYMBOL(oxygen_write16_masked);
 
@@ -79,7 +88,10 @@ void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
 			   u32 value, u32 mask)
 {
 	u32 tmp = inl(chip->addr + reg);
-	outl((tmp & ~mask) | (value & mask), chip->addr + reg);
+	tmp &= ~mask;
+	tmp |= value & mask;
+	outl(tmp, chip->addr + reg);
+	chip->saved_registers._32[reg / 4] = cpu_to_le32(tmp);
 }
 EXPORT_SYMBOL(oxygen_write32_masked);
 
@@ -128,8 +140,10 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
 		oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
 		/* require two "completed" writes, just to be sure */
 		if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 &&
-		    ++succeeded >= 2)
+		    ++succeeded >= 2) {
+			chip->saved_ac97_registers[codec][index / 2] = data;
 			return;
+		}
 	}
 	snd_printk(KERN_ERR "AC'97 write timeout\n");
 }
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 897697d43506d2354b2ffc954f58232d97b3c556..22f37851045e9639032b55960abcd3edb88c973e 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -32,7 +32,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("C-Media CMI8788 helper library");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 
 
 static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
@@ -173,7 +173,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry,
 	int i, j;
 
 	snd_iprintf(buffer, "CMI8788\n\n");
-	for (i = 0; i < 0x100; i += 0x10) {
+	for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
 		snd_iprintf(buffer, "%02x:", i);
 		for (j = 0; j < 0x10; ++j)
 			snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
@@ -314,6 +314,10 @@ static void oxygen_init(struct oxygen *chip)
 				    OXYGEN_SPDIF_LOCK_MASK |
 				    OXYGEN_SPDIF_RATE_MASK);
 	oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_STANDARD);
 	oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
 	oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
 	oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
@@ -455,7 +459,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
 	}
 
 	if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
-	    pci_resource_len(pci, 0) < 0x100) {
+	    pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) {
 		snd_printk(KERN_ERR "invalid PCI I/O range\n");
 		err = -ENXIO;
 		goto err_pci_regions;
@@ -534,3 +538,99 @@ void oxygen_pci_remove(struct pci_dev *pci)
 	pci_set_drvdata(pci, NULL);
 }
 EXPORT_SYMBOL(oxygen_pci_remove);
+
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct oxygen *chip = card->private_data;
+	unsigned int i, saved_interrupt_mask;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+	for (i = 0; i < PCM_COUNT; ++i)
+		if (chip->streams[i])
+			snd_pcm_suspend(chip->streams[i]);
+
+	if (chip->model->suspend)
+		chip->model->suspend(chip);
+
+	spin_lock_irq(&chip->reg_lock);
+	saved_interrupt_mask = chip->interrupt_mask;
+	chip->interrupt_mask = 0;
+	oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+	spin_unlock_irq(&chip->reg_lock);
+
+	synchronize_irq(chip->irq);
+	flush_scheduled_work();
+	chip->interrupt_mask = saved_interrupt_mask;
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+	return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_suspend);
+
+static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = {
+	0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff,
+	0x00300000, 0x00000fe4, 0x0ff7001f, 0x00000000
+};
+static const u32 ac97_registers_to_restore[2][0x40 / 32] = {
+	{ 0x18284fa2, 0x03060000 },
+	{ 0x00007fa6, 0x00200000 }
+};
+
+static inline int is_bit_set(const u32 *bitmap, unsigned int bit)
+{
+	return bitmap[bit / 32] & (1 << (bit & 31));
+}
+
+static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)
+{
+	unsigned int i;
+
+	oxygen_write_ac97(chip, codec, AC97_RESET, 0);
+	msleep(1);
+	for (i = 1; i < 0x40; ++i)
+		if (is_bit_set(ac97_registers_to_restore[codec], i))
+			oxygen_write_ac97(chip, codec, i * 2,
+					  chip->saved_ac97_registers[codec][i]);
+}
+
+int oxygen_pci_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct oxygen *chip = card->private_data;
+	unsigned int i;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+	if (pci_enable_device(pci) < 0) {
+		snd_printk(KERN_ERR "cannot reenable device");
+		snd_card_disconnect(card);
+		return -EIO;
+	}
+	pci_set_master(pci);
+
+	oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+	for (i = 0; i < OXYGEN_IO_SIZE; ++i)
+		if (is_bit_set(registers_to_restore, i))
+			oxygen_write8(chip, i, chip->saved_registers._8[i]);
+	if (chip->has_ac97_0)
+		oxygen_restore_ac97(chip, 0);
+	if (chip->has_ac97_1)
+		oxygen_restore_ac97(chip, 1);
+
+	if (chip->model->resume)
+		chip->model->resume(chip);
+
+	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_resume);
+#endif /* CONFIG_PM */
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index b17c405e069df5349daceba5db00416de012eb1b..c4ad65a3406fd61ea6bcb016d8b979def759691a 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -24,6 +24,16 @@
 #include <sound/pcm_params.h>
 #include "oxygen.h"
 
+/* most DMA channels have a 16-bit counter for 32-bit words */
+#define BUFFER_BYTES_MAX		((1 << 16) * 4)
+/* the multichannel DMA channel has a 24-bit counter */
+#define BUFFER_BYTES_MAX_MULTICH	((1 << 24) * 4)
+
+#define PERIOD_BYTES_MIN		64
+
+#define DEFAULT_BUFFER_BYTES		(BUFFER_BYTES_MAX / 2)
+#define DEFAULT_BUFFER_BYTES_MULTICH	(1024 * 1024)
+
 static const struct snd_pcm_hardware oxygen_stereo_hardware = {
 	.info = SNDRV_PCM_INFO_MMAP |
 		SNDRV_PCM_INFO_MMAP_VALID |
@@ -44,11 +54,11 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
 	.rate_max = 192000,
 	.channels_min = 2,
 	.channels_max = 2,
-	.buffer_bytes_max = 256 * 1024,
-	.period_bytes_min = 128,
-	.period_bytes_max = 128 * 1024,
+	.buffer_bytes_max = BUFFER_BYTES_MAX,
+	.period_bytes_min = PERIOD_BYTES_MIN,
+	.period_bytes_max = BUFFER_BYTES_MAX / 2,
 	.periods_min = 2,
-	.periods_max = 2048,
+	.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
 };
 static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
 	.info = SNDRV_PCM_INFO_MMAP |
@@ -70,11 +80,11 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
 	.rate_max = 192000,
 	.channels_min = 2,
 	.channels_max = 8,
-	.buffer_bytes_max = 2048 * 1024,
-	.period_bytes_min = 128,
-	.period_bytes_max = 256 * 1024,
+	.buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH,
+	.period_bytes_min = PERIOD_BYTES_MIN,
+	.period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2,
 	.periods_min = 2,
-	.periods_max = 16384,
+	.periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
 };
 static const struct snd_pcm_hardware oxygen_ac97_hardware = {
 	.info = SNDRV_PCM_INFO_MMAP |
@@ -88,11 +98,11 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
 	.rate_max = 48000,
 	.channels_min = 2,
 	.channels_max = 2,
-	.buffer_bytes_max = 256 * 1024,
-	.period_bytes_min = 128,
-	.period_bytes_max = 128 * 1024,
+	.buffer_bytes_max = BUFFER_BYTES_MAX,
+	.period_bytes_min = PERIOD_BYTES_MIN,
+	.period_bytes_max = BUFFER_BYTES_MAX / 2,
 	.periods_min = 2,
-	.periods_max = 2048,
+	.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
 };
 
 static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
@@ -155,6 +165,12 @@ static int oxygen_open(struct snd_pcm_substream *substream,
 		if (err < 0)
 			return err;
 	}
+	if (channel == PCM_MULTICH) {
+		err = snd_pcm_hw_constraint_minmax
+			(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0, 8192000);
+		if (err < 0)
+			return err;
+	}
 	snd_pcm_set_sync(substream);
 	chip->streams[channel] = substream;
 
@@ -517,6 +533,7 @@ static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
 		pausing = 0;
 		break;
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -663,12 +680,14 @@ int oxygen_pcm_init(struct oxygen *chip)
 			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
 						      SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      512 * 1024, 2048 * 1024);
+						      DEFAULT_BUFFER_BYTES_MULTICH,
+						      BUFFER_BYTES_MAX_MULTICH);
 		if (ins)
 			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
 						      SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      128 * 1024, 256 * 1024);
+						      DEFAULT_BUFFER_BYTES,
+						      BUFFER_BYTES_MAX);
 	}
 
 	outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF);
@@ -688,7 +707,8 @@ int oxygen_pcm_init(struct oxygen *chip)
 		strcpy(pcm->name, "Digital");
 		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      128 * 1024, 256 * 1024);
+						      DEFAULT_BUFFER_BYTES,
+						      BUFFER_BYTES_MAX);
 	}
 
 	if (chip->has_ac97_1) {
@@ -718,7 +738,8 @@ int oxygen_pcm_init(struct oxygen *chip)
 		strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
 		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      128 * 1024, 256 * 1024);
+						      DEFAULT_BUFFER_BYTES,
+						      BUFFER_BYTES_MAX);
 	}
 	return 0;
 }
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 7f84fa5deca2864473c00d60ff07ed602235204c..9a2c16bf94e0479ab359640808f829e1950b7d09 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -79,7 +79,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("Asus AVx00 driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -132,6 +132,9 @@ struct xonar_data {
 	u8 ext_power_int_reg;
 	u8 ext_power_bit;
 	u8 has_power;
+	u8 pcm1796_oversampling;
+	u8 cs4398_fm;
+	u8 cs4362a_fm;
 };
 
 static void pcm1796_write(struct oxygen *chip, unsigned int codec,
@@ -159,6 +162,14 @@ static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
 	oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
 }
 
+static void xonar_enable_output(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	msleep(data->anti_pop_delay);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
 static void xonar_common_init(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -170,32 +181,59 @@ static void xonar_common_init(struct oxygen *chip)
 		data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
 				     & data->ext_power_bit);
 	}
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_CS53x1_M_MASK | data->output_enable_bit);
 	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
 			      GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
 	oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
-	msleep(data->anti_pop_delay);
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
-	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+	xonar_enable_output(chip);
 }
 
-static void xonar_d2_init(struct oxygen *chip)
+static void update_pcm1796_volume(struct oxygen *chip)
 {
-	struct xonar_data *data = chip->model_data;
 	unsigned int i;
 
-	data->anti_pop_delay = 300;
-	data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
+	for (i = 0; i < 4; ++i) {
+		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
+		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
+	}
+}
+
+static void update_pcm1796_mute(struct oxygen *chip)
+{
+	unsigned int i;
+	u8 value;
+
+	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+	if (chip->dac_mute)
+		value |= PCM1796_MUTE;
+	for (i = 0; i < 4; ++i)
+		pcm1796_write(chip, i, 18, value);
+}
+
+static void pcm1796_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+	unsigned int i;
 
 	for (i = 0; i < 4; ++i) {
-		pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED |
-			      PCM1796_FMT_24_LJUST | PCM1796_ATLD);
 		pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
-		pcm1796_write(chip, i, 20, PCM1796_OS_64);
+		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
 		pcm1796_write(chip, i, 21, 0);
-		pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */
-		pcm1796_write(chip, i, 17, 0x0f);
 	}
+	update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */
+	update_pcm1796_volume(chip);
+}
+
+static void xonar_d2_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->anti_pop_delay = 300;
+	data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
+	data->pcm1796_oversampling = PCM1796_OS_64;
+
+	pcm1796_init(chip);
 
 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
@@ -217,31 +255,47 @@ static void xonar_d2x_init(struct oxygen *chip)
 	xonar_d2_init(chip);
 }
 
-static void xonar_dx_init(struct oxygen *chip)
+static void update_cs4362a_volumes(struct oxygen *chip)
 {
-	struct xonar_data *data = chip->model_data;
+	u8 mute;
 
-	data->anti_pop_delay = 800;
-	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;
+	mute = chip->dac_mute ? CS4362A_MUTE : 0;
+	cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
+	cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
+	cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
+	cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
+	cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
+	cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
+}
 
-	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
-		       OXYGEN_2WIRE_LENGTH_8 |
-		       OXYGEN_2WIRE_INTERRUPT_MASK |
-		       OXYGEN_2WIRE_SPEED_FAST);
+static void update_cs43xx_volume(struct oxygen *chip)
+{
+	cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
+	cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
+	update_cs4362a_volumes(chip);
+}
+
+static void update_cs43xx_mute(struct oxygen *chip)
+{
+	u8 reg;
+
+	reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
+	if (chip->dac_mute)
+		reg |= CS4398_MUTE_B | CS4398_MUTE_A;
+	cs4398_write(chip, 4, reg);
+	update_cs4362a_volumes(chip);
+}
+
+static void cs43xx_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
 
 	/* set CPEN (control port mode) and power down */
 	cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
 	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
 	/* configure */
-	cs4398_write(chip, 2, CS4398_FM_SINGLE |
-		     CS4398_DEM_NONE | CS4398_DIF_LJUST);
+	cs4398_write(chip, 2, data->cs4398_fm);
 	cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
-	cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE);
-	cs4398_write(chip, 5, 0xfe);
-	cs4398_write(chip, 6, 0xfe);
 	cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
 		     CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
 	cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
@@ -249,21 +303,35 @@ static void xonar_dx_init(struct oxygen *chip)
 		      CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
 	cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
 	cs4362a_write(chip, 0x05, 0);
-	cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE |
-		      CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
-	cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE |
-		      CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
-	cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE |
-		      CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
-	cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE);
+	cs4362a_write(chip, 0x06, data->cs4362a_fm);
+	cs4362a_write(chip, 0x09, data->cs4362a_fm);
+	cs4362a_write(chip, 0x0c, data->cs4362a_fm);
+	update_cs43xx_volume(chip);
+	update_cs43xx_mute(chip);
 	/* clear power down */
 	cs4398_write(chip, 8, CS4398_CPEN);
 	cs4362a_write(chip, 0x01, CS4362A_CPEN);
+}
+
+static void xonar_dx_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->anti_pop_delay = 800;
+	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;
+	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;
+
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_FAST);
+
+	cs43xx_init(chip);
 
 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
 			  GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
@@ -291,37 +359,28 @@ static void xonar_dx_cleanup(struct oxygen *chip)
 	oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
 }
 
-static void set_pcm1796_params(struct oxygen *chip,
-			       struct snd_pcm_hw_params *params)
+static void xonar_d2_resume(struct oxygen *chip)
 {
-	unsigned int i;
-	u8 value;
-
-	value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
-	for (i = 0; i < 4; ++i)
-		pcm1796_write(chip, i, 20, value);
+	pcm1796_init(chip);
+	xonar_enable_output(chip);
 }
 
-static void update_pcm1796_volume(struct oxygen *chip)
+static void xonar_dx_resume(struct oxygen *chip)
 {
-	unsigned int i;
-
-	for (i = 0; i < 4; ++i) {
-		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
-		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
-	}
+	cs43xx_init(chip);
+	xonar_enable_output(chip);
 }
 
-static void update_pcm1796_mute(struct oxygen *chip)
+static void set_pcm1796_params(struct oxygen *chip,
+			       struct snd_pcm_hw_params *params)
 {
+	struct xonar_data *data = chip->model_data;
 	unsigned int i;
-	u8 value;
 
-	value = PCM1796_FMT_24_LJUST | PCM1796_ATLD;
-	if (chip->dac_mute)
-		value |= PCM1796_MUTE;
+	data->pcm1796_oversampling =
+		params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
 	for (i = 0; i < 4; ++i)
-		pcm1796_write(chip, i, 18, value);
+		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
 }
 
 static void set_cs53x1_params(struct oxygen *chip,
@@ -342,55 +401,24 @@ static void set_cs53x1_params(struct oxygen *chip,
 static void set_cs43xx_params(struct oxygen *chip,
 			      struct snd_pcm_hw_params *params)
 {
-	u8 fm_cs4398, fm_cs4362a;
+	struct xonar_data *data = chip->model_data;
 
-	fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST;
-	fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+	data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST;
+	data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
 	if (params_rate(params) <= 50000) {
-		fm_cs4398 |= CS4398_FM_SINGLE;
-		fm_cs4362a |= CS4362A_FM_SINGLE;
+		data->cs4398_fm |= CS4398_FM_SINGLE;
+		data->cs4362a_fm |= CS4362A_FM_SINGLE;
 	} else if (params_rate(params) <= 100000) {
-		fm_cs4398 |= CS4398_FM_DOUBLE;
-		fm_cs4362a |= CS4362A_FM_DOUBLE;
+		data->cs4398_fm |= CS4398_FM_DOUBLE;
+		data->cs4362a_fm |= CS4362A_FM_DOUBLE;
 	} else {
-		fm_cs4398 |= CS4398_FM_QUAD;
-		fm_cs4362a |= CS4362A_FM_QUAD;
+		data->cs4398_fm |= CS4398_FM_QUAD;
+		data->cs4362a_fm |= CS4362A_FM_QUAD;
 	}
-	cs4398_write(chip, 2, fm_cs4398);
-	cs4362a_write(chip, 0x06, fm_cs4362a);
-	cs4362a_write(chip, 0x09, fm_cs4362a);
-	cs4362a_write(chip, 0x0c, fm_cs4362a);
-}
-
-static void update_cs4362a_volumes(struct oxygen *chip)
-{
-	u8 mute;
-
-	mute = chip->dac_mute ? CS4362A_MUTE : 0;
-	cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
-	cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
-	cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
-	cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
-	cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
-	cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
-}
-
-static void update_cs43xx_volume(struct oxygen *chip)
-{
-	cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
-	cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
-	update_cs4362a_volumes(chip);
-}
-
-static void update_cs43xx_mute(struct oxygen *chip)
-{
-	u8 reg;
-
-	reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
-	if (chip->dac_mute)
-		reg |= CS4398_MUTE_B | CS4398_MUTE_A;
-	cs4398_write(chip, 4, reg);
-	update_cs4362a_volumes(chip);
+	cs4398_write(chip, 2, data->cs4398_fm);
+	cs4362a_write(chip, 0x06, data->cs4362a_fm);
+	cs4362a_write(chip, 0x09, data->cs4362a_fm);
+	cs4362a_write(chip, 0x0c, data->cs4362a_fm);
 }
 
 static void xonar_gpio_changed(struct oxygen *chip)
@@ -535,6 +563,8 @@ static const struct oxygen_model xonar_models[] = {
 		.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,
@@ -563,6 +593,8 @@ static const struct oxygen_model xonar_models[] = {
 		.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,
@@ -592,6 +624,8 @@ static const struct oxygen_model xonar_models[] = {
 		.control_filter = xonar_dx_control_filter,
 		.mixer_init = xonar_dx_mixer_init,
 		.cleanup = xonar_dx_cleanup,
+		.suspend = xonar_dx_cleanup,
+		.resume = xonar_dx_resume,
 		.set_dac_params = set_cs43xx_params,
 		.set_adc_params = set_cs53x1_params,
 		.update_dac_volume = update_cs43xx_volume,
@@ -636,6 +670,10 @@ static struct pci_driver xonar_driver = {
 	.id_table = xonar_ids,
 	.probe = xonar_probe,
 	.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+	.suspend = oxygen_pci_suspend,
+	.resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_xonar_init(void)
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 7fdcdc8c6b6478bbce2fff1c3f784ed90fb51a37..2c7e25336795d6bfb6588019069ff77947163e61 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -516,7 +516,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
 	int capture_mask = 0;
 	int playback_mask = 0;
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	struct timeval my_tv1, my_tv2;
 	do_gettimeofday(&my_tv1);
 #endif
@@ -623,7 +623,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
 
 	mutex_unlock(&mgr->setup_mutex);
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	do_gettimeofday(&my_tv2);
 	snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n",
 		    (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 78aa81feaa4a9edfccb8fde5444b580d3afbb1a6..abe5c59b72dfbe5366c5932911408e0848a1e0ed 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -473,7 +473,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
 [CMD_AUDIO_LEVEL_ADJUST] =		{ 0xc22000, 0, RMH_SSIZE_FIXED },
 };
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 static char* cmd_names[] = {
 [CMD_VERSION] =				"CMD_VERSION",
 [CMD_SUPPORTED] =			"CMD_SUPPORTED",
@@ -549,7 +549,7 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
 				}
 			}
 		}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 		if (rmh->cmd_idx < CMD_LAST_INDEX)
 			snd_printdd("    stat[%d]=%x\n", i, data);
 #endif
@@ -597,7 +597,7 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
 		data |= 0x008000;	/* MASK_MORE_THAN_1_WORD_COMMAND */
 	else
 		data &= 0xff7fff;	/* MASK_1_WORD_COMMAND */
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	if (rmh->cmd_idx < CMD_LAST_INDEX)
 		snd_printdd("MSG cmd[0]=%x (%s)\n", data, cmd_names[rmh->cmd_idx]);
 #endif
@@ -624,7 +624,7 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
 		for (i=1; i < rmh->cmd_len; i++) {
 			/* send other words */
 			data = rmh->cmd[i];
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 			if (rmh->cmd_idx < CMD_LAST_INDEX)
 				snd_printdd("    cmd[%d]=%x\n", i, data);
 #endif
@@ -847,7 +847,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
 	int state, i, err;
 	int audio_mask;
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	struct timeval my_tv1, my_tv2;
 	do_gettimeofday(&my_tv1);
 #endif
@@ -894,7 +894,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
 		if (err)
 			return err;
 	}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	do_gettimeofday(&my_tv2);
 	snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n",
 		    (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
@@ -951,7 +951,7 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
 				  enum pcxhr_async_err_src err_src, int pipe,
 				  int is_capture)
 {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	static char* err_src_name[] = {
 		[PCXHR_ERR_PIPE]	= "Pipe",
 		[PCXHR_ERR_STREAM]	= "Stream",
@@ -1169,7 +1169,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
 				    mgr->dsp_time_last, dsp_time_new);
 			mgr->dsp_time_err++;
 		}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 		if (dsp_time_diff == 0)
 			snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new);
 		else if (dsp_time_diff >= (2*PCXHR_GRANULARITY))
@@ -1208,7 +1208,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
 		mgr->src_it_dsp = reg;
 		tasklet_hi_schedule(&mgr->msg_taskq);
 	}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	if (reg & PCXHR_FATAL_DSP_ERR)
 		snd_printdd("FATAL DSP ERROR : %x\n", reg);
 #endif
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index bbcee2c09ae42e15085cc91d0bcb1625b50118dd..a69b4206c69e205f9a88726f63182f75b36491ee 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -1590,7 +1590,10 @@ static int snd_trident_trigger(struct snd_pcm_substream *substream,
 	if (spdif_flag) {
 		if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
 			outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
-			outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+			val = trident->spdif_pcm_ctrl;
+			if (!go)
+				val &= ~(0x28);
+			outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
 		} else {
 			outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
 			val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index df9b487fa17e7ca582e1eaae362656ca591e6620..3fd7f1b29b0f57840192aff0e4a97294134c0a5b 100644
--- a/sound/pci/trident/trident_memory.c
+++ b/sound/pci/trident/trident_memory.c
@@ -310,181 +310,3 @@ int snd_trident_free_pages(struct snd_trident *trident,
 	mutex_unlock(&hdr->block_mutex);
 	return 0;
 }
-
-
-/*----------------------------------------------------------------
- * memory allocation using multiple pages (for synth)
- *----------------------------------------------------------------
- * Unlike the DMA allocation above, non-contiguous pages are
- * assigned to TLB.
- *----------------------------------------------------------------*/
-
-/*
- */
-static int synth_alloc_pages(struct snd_trident *hw, struct snd_util_memblk *blk);
-static int synth_free_pages(struct snd_trident *hw, struct snd_util_memblk *blk);
-
-/*
- * allocate a synth sample area
- */
-struct snd_util_memblk *
-snd_trident_synth_alloc(struct snd_trident *hw, unsigned int size)
-{
-	struct snd_util_memblk *blk;
-	struct snd_util_memhdr *hdr = hw->tlb.memhdr; 
-
-	mutex_lock(&hdr->block_mutex);
-	blk = __snd_util_mem_alloc(hdr, size);
-	if (blk == NULL) {
-		mutex_unlock(&hdr->block_mutex);
-		return NULL;
-	}
-	if (synth_alloc_pages(hw, blk)) {
-		__snd_util_mem_free(hdr, blk);
-		mutex_unlock(&hdr->block_mutex);
-		return NULL;
-	}
-	mutex_unlock(&hdr->block_mutex);
-	return blk;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_alloc);
-
-/*
- * free a synth sample area
- */
-int
-snd_trident_synth_free(struct snd_trident *hw, struct snd_util_memblk *blk)
-{
-	struct snd_util_memhdr *hdr = hw->tlb.memhdr; 
-
-	mutex_lock(&hdr->block_mutex);
-	synth_free_pages(hw, blk);
-	 __snd_util_mem_free(hdr, blk);
-	mutex_unlock(&hdr->block_mutex);
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_free);
-
-/*
- * reset TLB entry and free kernel page
- */
-static void clear_tlb(struct snd_trident *trident, int page)
-{
-	void *ptr = page_to_ptr(trident, page);
-	dma_addr_t addr = page_to_addr(trident, page);
-	set_silent_tlb(trident, page);
-	if (ptr) {
-		struct snd_dma_buffer dmab;
-		dmab.dev.type = SNDRV_DMA_TYPE_DEV;
-		dmab.dev.dev = snd_dma_pci_data(trident->pci);
-		dmab.area = ptr;
-		dmab.addr = addr;
-		dmab.bytes = ALIGN_PAGE_SIZE;
-		snd_dma_free_pages(&dmab);
-	}
-}
-
-/* check new allocation range */
-static void get_single_page_range(struct snd_util_memhdr *hdr,
-				  struct snd_util_memblk *blk,
-				  int *first_page_ret, int *last_page_ret)
-{
-	struct list_head *p;
-	struct snd_util_memblk *q;
-	int first_page, last_page;
-	first_page = firstpg(blk);
-	if ((p = blk->list.prev) != &hdr->block) {
-		q = list_entry(p, struct snd_util_memblk, list);
-		if (lastpg(q) == first_page)
-			first_page++;  /* first page was already allocated */
-	}
-	last_page = lastpg(blk);
-	if ((p = blk->list.next) != &hdr->block) {
-		q = list_entry(p, struct snd_util_memblk, list);
-		if (firstpg(q) == last_page)
-			last_page--; /* last page was already allocated */
-	}
-	*first_page_ret = first_page;
-	*last_page_ret = last_page;
-}
-
-/*
- * allocate kernel pages and assign them to TLB
- */
-static int synth_alloc_pages(struct snd_trident *hw, struct snd_util_memblk *blk)
-{
-	int page, first_page, last_page;
-	struct snd_dma_buffer dmab;
-
-	firstpg(blk) = get_aligned_page(blk->offset);
-	lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1);
-	get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page);
-
-	/* allocate a kernel page for each Trident page -
-	 * fortunately Trident page size and kernel PAGE_SIZE is identical!
-	 */
-	for (page = first_page; page <= last_page; page++) {
-		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(hw->pci),
-					ALIGN_PAGE_SIZE, &dmab) < 0)
-			goto __fail;
-		if (! is_valid_page(dmab.addr)) {
-			snd_dma_free_pages(&dmab);
-			goto __fail;
-		}
-		set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr);
-	}
-	return 0;
-
-__fail:
-	/* release allocated pages */
-	last_page = page - 1;
-	for (page = first_page; page <= last_page; page++)
-		clear_tlb(hw, page);
-
-	return -ENOMEM;
-}
-
-/*
- * free pages
- */
-static int synth_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk)
-{
-	int page, first_page, last_page;
-
-	get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page);
-	for (page = first_page; page <= last_page; page++)
-		clear_tlb(trident, page);
-
-	return 0;
-}
-
-/*
- * copy_from_user(blk + offset, data, size)
- */
-int snd_trident_synth_copy_from_user(struct snd_trident *trident,
-				     struct snd_util_memblk *blk,
-				     int offset, const char __user *data, int size)
-{
-	int page, nextofs, end_offset, temp, temp1;
-
-	offset += blk->offset;
-	end_offset = offset + size;
-	page = get_aligned_page(offset) + 1;
-	do {
-		nextofs = aligned_page_offset(page);
-		temp = nextofs - offset;
-		temp1 = end_offset - offset;
-		if (temp1 < temp)
-			temp = temp1;
-		if (copy_from_user(offset_ptr(trident, offset), data, temp))
-			return -EFAULT;
-		offset = nextofs;
-		data += temp;
-		page++;
-	} while (offset < end_offset);
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index b585cc3e4c472eea6434e89d9ed463038f6a61c9..6781be9e3078ac702364770f407fbc4458486882 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -1756,6 +1756,12 @@ static struct ac97_quirk ac97_quirks[] = {
 		.name = "ECS L7VMM2",
 		.type = AC97_TUNE_HP_ONLY
 	},
+	{
+		.subvendor = 0x1019,
+		.subdevice = 0x1841,
+		.name = "ECS K7VTA3",
+		.type = AC97_TUNE_HP_ONLY
+	},
 	{
 		.subvendor = 0x1849,
 		.subdevice = 0x3059,
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 29b3056c51098b708aaa6e860af148a011bae098..7129df5f315b32d01384560e1e2b0103c0e685f9 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -2205,6 +2205,7 @@ static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
 	for (reg = 0x80; reg < 0xc0; reg += 4)
 		snd_ymfpci_writel(chip, reg, 0);
 	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0x3fff3fff);
 	snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff);
 	snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff);
 	snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
@@ -2324,6 +2325,7 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
 		chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
 	chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
 	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+	snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
 	snd_ymfpci_disable_dsp(chip);
 	pci_disable_device(pci);
 	pci_save_state(pci);
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
index c9fa1a2bc58b852e3e4680589d6c1b1b71f6145b..7fbb190adf6d1a256b59f4b0d442e9fa0a4e1011 100644
--- a/sound/pcmcia/Kconfig
+++ b/sound/pcmcia/Kconfig
@@ -1,11 +1,16 @@
 # ALSA PCMCIA drivers
 
-menu "PCMCIA devices"
-	depends on SND!=n && PCMCIA
+menuconfig SND_PCMCIA
+	bool "PCMCIA sound devices"
+	depends on PCMCIA
+	default y
+	help
+	  Support for sound devices connected via the PCMCIA bus.
+
+if SND_PCMCIA && PCMCIA
 
 config SND_VXPOCKET
 	tristate "Digigram VXpocket"
-	depends on SND && PCMCIA
 	select SND_VX_LIB
 	help
 	  Say Y here to include support for Digigram VXpocket and
@@ -16,7 +21,6 @@ config SND_VXPOCKET
 
 config SND_PDAUDIOCF
 	tristate "Sound Core PDAudioCF"
-	depends on SND && PCMCIA
 	select SND_PCM
 	help
 	  Say Y here to include support for Sound Core PDAudioCF
@@ -25,4 +29,5 @@ config SND_PDAUDIOCF
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-pdaudiocf.
 
-endmenu
+endif	# SND_PCMCIA
+
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c
index 157b0b539f39d33cfc3b8f7bce35b1c288649bd6..99bf2a65a6f5ce82260b2f7d0f8fdbf56b804c7f 100644
--- a/sound/pcmcia/vx/vxp_ops.c
+++ b/sound/pcmcia/vx/vxp_ops.c
@@ -151,7 +151,7 @@ static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *
 	unsigned int i;
 	int c;
 	int regCSUER, regRUER;
-	unsigned char *image;
+	const unsigned char *image;
 	unsigned char data;
 
 	/* Switch to programmation mode */
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig
index cacb0b1368833d6d2055ee0bffdba03db273dec1..777de2b171781b78eca1a347238efc6a585624b5 100644
--- a/sound/ppc/Kconfig
+++ b/sound/ppc/Kconfig
@@ -1,17 +1,17 @@
 # ALSA PowerMac drivers
 
-menu "ALSA PowerMac devices"
-	depends on SND!=n && PPC
-
-comment "ALSA PowerMac requires I2C"
-	depends on SND && I2C=n
+menuconfig SND_PPC
+	bool "PowerPC sound devices"
+	depends on PPC64 || PPC32
+	default y
+	help
+	  Support for sound devices specific to PowerPC architectures.
 
-comment "ALSA PowerMac requires INPUT"
-	depends on SND && INPUT=n
+if SND_PPC
 
 config SND_POWERMAC
 	tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)"
-	depends on SND && I2C && INPUT && PPC_PMAC
+	depends on I2C && INPUT && PPC_PMAC
 	select SND_PCM
 	help
 	  Say Y here to include support for the integrated sound device.
@@ -32,14 +32,9 @@ config SND_POWERMAC_AUTO_DRC
 	  Note that you can turn on/off DRC manually even without this
 	  option.
 
-endmenu
-
-menu "ALSA PowerPC devices"
-	depends on SND!=n && ( PPC64 || PPC32 )
-
 config SND_PS3
 	tristate "PS3 Audio support"
-	depends on SND && PS3_PS3AV
+	depends on PS3_PS3AV
 	select SND_PCM
 	default m
 	help
@@ -52,4 +47,5 @@ config SND_PS3_DEFAULT_START_DELAY
 	int "Startup delay time in ms"
 	depends on SND_PS3
 	default "2000"
-endmenu
+
+endif	# SND_PPC
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index ca9452901a50517e139f1e7784e327659b0bff48..8a5b29031933a09f1a9d1ef047ee2a1db93364c4 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -249,9 +249,7 @@ int __init snd_pmac_daca_init(struct snd_pmac *chip)
 	int i, err;
 	struct pmac_daca *mix;
 
-#ifdef CONFIG_KMOD
 	request_module("i2c-powermac");
-#endif /* CONFIG_KMOD */
 
 	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
 	if (! mix)
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 3f8d7164cef9438407b3f00128739438f92069b7..009df8dd37a8483a10987b948ecd3329dd1e556c 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1350,9 +1350,7 @@ int __init snd_pmac_tumbler_init(struct snd_pmac *chip)
 	struct device_node *tas_node, *np;
 	char *chipname;
 
-#ifdef CONFIG_KMOD
 	request_module("i2c-powermac");
-#endif /* CONFIG_KMOD */
 
 	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
 	if (! mix)
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index b7e08ef22a940ef2d567c9c06c0d6816b8f33c1a..cfc1439858029e32956906947ecdf2755299059d 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -1,14 +1,22 @@
 # ALSA SH drivers
 
-menu "SUPERH devices"
-	depends on SND!=n && SUPERH
+menuconfig SND_SUPERH
+	bool "SUPERH sound devices"
+	depends on SUPERH
+	default y
+	help
+	  Support for sound devices specific to SUPERH architectures.
+	  Drivers that are implemented on ASoC can be found in
+	  "ALSA for SoC audio support" section.
+
+if SND_SUPERH
 
 config SND_AICA
 	tristate "Dreamcast Yamaha AICA sound"
-	depends on SH_DREAMCAST && SND
+	depends on SH_DREAMCAST
 	select SND_PCM
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
-endmenu
+endif	# SND_SUPERH
 
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 18f28ac4bfe82997733df97c1c1eaaf05dc41adb..f743530add8f2bf8a9b58a345ec515b62e3a056b 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -2,15 +2,8 @@
 # SoC audio configuration
 #
 
-menu "System on Chip audio support"
-	depends on SND!=n
-
-config SND_SOC_AC97_BUS
-	bool
-
-config SND_SOC
+menuconfig SND_SOC
 	tristate "ALSA for SoC audio support"
-	depends on SND
 	select SND_PCM
 	---help---
 
@@ -23,8 +16,15 @@ config SND_SOC
 	  This ASoC audio support can also be built as a module.  If so, the module
 	  will be called snd-soc-core.
 
+if SND_SOC
+
+config SND_SOC_AC97_BUS
+	bool
+
 # All the supported Soc's
+source "sound/soc/at32/Kconfig"
 source "sound/soc/at91/Kconfig"
+source "sound/soc/au1x/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/sh/Kconfig"
@@ -35,4 +35,5 @@ source "sound/soc/omap/Kconfig"
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
 
-endmenu
+endif	# SND_SOC
+
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 782db2127108fd26c19a0bb6bf2ee378cbd4d0ac..933a66d30804950b96b1e185285880bf204d673f 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,5 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
-obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
+obj-$(CONFIG_SND_SOC)	+= codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
+obj-$(CONFIG_SND_SOC)	+= omap/ au1x/
diff --git a/sound/soc/at32/Kconfig b/sound/soc/at32/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..b0765e86c0857f6ab155ff9f814fcb237126c1d4
--- /dev/null
+++ b/sound/soc/at32/Kconfig
@@ -0,0 +1,34 @@
+config SND_AT32_SOC
+        tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
+        depends on AVR32 && SND_SOC
+        help
+          Say Y or M if you want to add support for codecs attached to 
+          the AT32 SSC interface.  You will also need to
+          to select the audio interfaces to support below.
+
+
+config SND_AT32_SOC_SSC
+        tristate
+
+
+
+config SND_AT32_SOC_PLAYPAQ
+        tristate "SoC Audio support for PlayPaq with WM8510"
+        depends on SND_AT32_SOC && BOARD_PLAYPAQ
+        select SND_AT32_SOC_SSC
+        select SND_SOC_WM8510
+        help
+          Say Y or M here if you want to add support for SoC audio
+          on the LRS PlayPaq.
+
+
+
+config SND_AT32_SOC_PLAYPAQ_SLAVE
+        bool "Run CODEC on PlayPaq in slave mode"
+        depends on SND_AT32_SOC_PLAYPAQ
+        default n
+        help
+          Say Y if you want to run with the AT32 SSC generating the BCLK
+          and FRAME signals on the PlayPaq.  Unless you want to play
+          with the AT32 as the SSC master, you probably want to say N here,
+          as this will give you better sound quality.
diff --git a/sound/soc/at32/Makefile b/sound/soc/at32/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..c03e55ececeb1d433c67142844b4d2329b95bba3
--- /dev/null
+++ b/sound/soc/at32/Makefile
@@ -0,0 +1,11 @@
+# AT32 Platform Support
+snd-soc-at32-objs := at32-pcm.o
+snd-soc-at32-ssc-objs := at32-ssc.o
+
+obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
+obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
+
+# AT32 Machine Support
+snd-soc-playpaq-objs := playpaq_wm8510.o
+
+obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c
new file mode 100644
index 0000000000000000000000000000000000000000..435f1daf177c39e767cd4a7b6999f6d81d9847dc
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.c
@@ -0,0 +1,491 @@
+/* sound/soc/at32/at32-pcm.c
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.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.
+ *
+ * Note that this is basically a port of the sound/soc/at91-pcm.c to
+ * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "at32-pcm.h"
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ *	 them against real values for AT32
+ */
+static const struct snd_pcm_hardware at32_pcm_hardware = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		 SNDRV_PCM_INFO_PAUSE),
+
+	.formats = SNDRV_PCM_FMTBIT_S16,
+	.period_bytes_min = 32,
+	.period_bytes_max = 8192,	/* 512 frames * 16 bytes / frame */
+	.periods_min = 2,
+	.periods_max = 1024,
+	.buffer_bytes_max = 32 * 1024,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct at32_runtime_data {
+	struct at32_pcm_dma_params *params;
+	dma_addr_t dma_buffer;	/* physical address of DMA buffer */
+	dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
+	size_t period_size;
+
+	dma_addr_t period_ptr;	/* physical address of next period */
+	int periods;		/* period index of period_ptr */
+
+	/* Save PDC registers (for power management) */
+	u32 pdc_xpr_save;
+	u32 pdc_xcr_save;
+	u32 pdc_xnpr_save;
+	u32 pdc_xncr_save;
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Helper functions
+\*--------------------------------------------------------------------------*/
+static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
+	size_t size = at32_pcm_hardware.buffer_bytes_max;
+
+	dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dmabuf->dev.dev = pcm->card->dev;
+	dmabuf->private_data = NULL;
+	dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
+					  &dmabuf->addr, GFP_KERNEL);
+	pr_debug("at32_pcm: preallocate_dma_buffer: "
+		 "area=%p, addr=%p, size=%ld\n",
+		 (void *)dmabuf->area, (void *)dmabuf->addr, size);
+
+	if (!dmabuf->area)
+		return -ENOMEM;
+
+	dmabuf->bytes = size;
+	return 0;
+}
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *rtd = substream->runtime;
+	struct at32_runtime_data *prtd = rtd->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+	static int count;
+
+	count++;
+	if (ssc_sr & params->mask->ssc_endbuf) {
+		pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
+			   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			   "underrun" : "overrun", params->name, ssc_sr, count);
+
+		/* re-start the PDC */
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_disable);
+		prtd->period_ptr += prtd->period_size;
+		if (prtd->period_ptr >= prtd->dma_buffer_end)
+			prtd->period_ptr = prtd->dma_buffer;
+
+
+		ssc_writex(params->ssc->regs, params->pdc->xpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xcr,
+			   prtd->period_size / params->pdc_xfer_size);
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_enable);
+	}
+
+
+	if (ssc_sr & params->mask->ssc_endx) {
+		/* Load the PDC next pointer and counter registers */
+		prtd->period_ptr += prtd->period_size;
+		if (prtd->period_ptr >= prtd->dma_buffer_end)
+			prtd->period_ptr = prtd->dma_buffer;
+		ssc_writex(params->ssc->regs, params->pdc->xnpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xncr,
+			   prtd->period_size / params->pdc_xfer_size);
+	}
+
+
+	snd_pcm_period_elapsed(substream);
+}
+
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct at32_runtime_data *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	/* this may get called several times by oss emulation
+	 * with different params
+	 */
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	prtd->params = rtd->dai->cpu_dai->dma_data;
+	prtd->params->dma_intr_handler = at32_pcm_dma_irq;
+
+	prtd->dma_buffer = runtime->dma_addr;
+	prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+	prtd->period_size = params_period_bytes(params);
+
+	pr_debug("hw_params: DMA for %s initialized "
+		 "(dma_bytes=%ld, period_size=%ld)\n",
+		 prtd->params->name, runtime->dma_bytes, prtd->period_size);
+
+	return 0;
+}
+
+
+
+static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct at32_runtime_data *prtd = substream->runtime->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+
+	if (params != NULL) {
+		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+			   params->mask->pdc_disable);
+		prtd->params->dma_intr_handler = NULL;
+	}
+
+	return 0;
+}
+
+
+
+static int at32_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct at32_runtime_data *prtd = substream->runtime->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+
+	ssc_writex(params->ssc->regs, SSC_IDR,
+		   params->mask->ssc_endx | params->mask->ssc_endbuf);
+	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+		   params->mask->pdc_disable);
+
+	return 0;
+}
+
+
+static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *rtd = substream->runtime;
+	struct at32_runtime_data *prtd = rtd->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+	int ret = 0;
+
+	pr_debug("at32_pcm_trigger: buffer_size = %ld, "
+		 "dma_area = %p, dma_bytes = %ld\n",
+		 rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		prtd->period_ptr = prtd->dma_buffer;
+
+		ssc_writex(params->ssc->regs, params->pdc->xpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xcr,
+			   prtd->period_size / params->pdc_xfer_size);
+
+		prtd->period_ptr += prtd->period_size;
+		ssc_writex(params->ssc->regs, params->pdc->xnpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xncr,
+			   prtd->period_size / params->pdc_xfer_size);
+
+		pr_debug("trigger: period_ptr=%lx, xpr=%x, "
+			 "xcr=%d, xnpr=%x, xncr=%d\n",
+			 (unsigned long)prtd->period_ptr,
+			 ssc_readx(params->ssc->regs, params->pdc->xpr),
+			 ssc_readx(params->ssc->regs, params->pdc->xcr),
+			 ssc_readx(params->ssc->regs, params->pdc->xnpr),
+			 ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+		ssc_writex(params->ssc->regs, SSC_IER,
+			   params->mask->ssc_endx | params->mask->ssc_endbuf);
+		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+			   params->mask->pdc_enable);
+
+		pr_debug("sr=%x, imr=%x\n",
+			 ssc_readx(params->ssc->regs, SSC_SR),
+			 ssc_readx(params->ssc->regs, SSC_IER));
+		break;		/* SNDRV_PCM_TRIGGER_START */
+
+
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_disable);
+		break;
+
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_enable);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+
+
+static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct at32_runtime_data *prtd = runtime->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+	dma_addr_t ptr;
+	snd_pcm_uframes_t x;
+
+	ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+	x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+	if (x == runtime->buffer_size)
+		x = 0;
+
+	return x;
+}
+
+
+
+static int at32_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct at32_runtime_data *prtd;
+	int ret = 0;
+
+	snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
+
+	/* ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto out;
+
+	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+	if (prtd == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	runtime->private_data = prtd;
+
+
+out:
+	return ret;
+}
+
+
+
+static int at32_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct at32_runtime_data *prtd = substream->runtime->private_data;
+
+	kfree(prtd);
+	return 0;
+}
+
+
+static int at32_pcm_mmap(struct snd_pcm_substream *substream,
+			 struct vm_area_struct *vma)
+{
+	return remap_pfn_range(vma, vma->vm_start,
+			       substream->dma_buffer.addr >> PAGE_SHIFT,
+			       vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+
+
+static struct snd_pcm_ops at32_pcm_ops = {
+	.open = at32_pcm_open,
+	.close = at32_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = at32_pcm_hw_params,
+	.hw_free = at32_pcm_hw_free,
+	.prepare = at32_pcm_prepare,
+	.trigger = at32_pcm_trigger,
+	.pointer = at32_pcm_pointer,
+	.mmap = at32_pcm_mmap,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+static u64 at32_pcm_dmamask = 0xffffffff;
+
+static int at32_pcm_new(struct snd_card *card,
+			struct snd_soc_dai *dai,
+			struct snd_pcm *pcm)
+{
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &at32_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (dai->playback.channels_min) {
+		ret = at32_pcm_preallocate_dma_buffer(
+			  pcm, SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
+		ret = at32_pcm_preallocate_dma_buffer(
+			  pcm, SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+
+
+out:
+	return ret;
+}
+
+
+
+static void at32_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 == NULL)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+				  buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_pcm_suspend(struct platform_device *pdev,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = dai->runtime;
+	struct at32_runtime_data *prtd;
+	struct at32_pcm_dma_params *params;
+
+	if (runtime == NULL)
+		return 0;
+	prtd = runtime->private_data;
+	params = prtd->params;
+
+	/* Disable the PDC and save the PDC registers */
+	ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
+
+	prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+	prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+	prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+	prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+	return 0;
+}
+
+
+
+static int at32_pcm_resume(struct platform_device *pdev,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = dai->runtime;
+	struct at32_runtime_data *prtd;
+	struct at32_pcm_dma_params *params;
+
+	if (runtime == NULL)
+		return 0;
+	prtd = runtime->private_data;
+	params = prtd->params;
+
+	/* Restore the PDC registers and enable the PDC */
+	ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+	ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+	return 0;
+}
+#else /* CONFIG_PM */
+#  define at32_pcm_suspend	NULL
+#  define at32_pcm_resume	NULL
+#endif /* CONFIG_PM */
+
+
+
+struct snd_soc_platform at32_soc_platform = {
+	.name = "at32-audio",
+	.pcm_ops = &at32_pcm_ops,
+	.pcm_new = at32_pcm_new,
+	.pcm_free = at32_pcm_free_dma_buffers,
+	.suspend = at32_pcm_suspend,
+	.resume = at32_pcm_resume,
+};
+EXPORT_SYMBOL_GPL(at32_soc_platform);
+
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("Atmel AT32 PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-pcm.h b/sound/soc/at32/at32-pcm.h
new file mode 100644
index 0000000000000000000000000000000000000000..2a52430417da82dd9c3a5abd092caa754ba2d71e
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.h
@@ -0,0 +1,79 @@
+/* sound/soc/at32/at32-pcm.h
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.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.
+ */
+
+#ifndef __SOUND_SOC_AT32_AT32_PCM_H
+#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
+
+#include <linux/atmel-ssc.h>
+
+
+/*
+ * Registers and status bits that are required by the PCM driver
+ * TODO: Is ptcr really used?
+ */
+struct at32_pdc_regs {
+	u32 xpr;		/* PDC RX/TX pointer */
+	u32 xcr;		/* PDC RX/TX counter */
+	u32 xnpr;		/* PDC next RX/TX pointer */
+	u32 xncr;		/* PDC next RX/TX counter */
+	u32 ptcr;		/* PDC transfer control */
+};
+
+
+
+/*
+ * SSC mask info
+ */
+struct at32_ssc_mask {
+	u32 ssc_enable;		/* SSC RX/TX enable */
+	u32 ssc_disable;	/* SSC RX/TX disable */
+	u32 ssc_endx;		/* SSC ENDTX or ENDRX */
+	u32 ssc_endbuf;		/* SSC TXBUFF or RXBUFF */
+	u32 pdc_enable;		/* PDC RX/TX enable */
+	u32 pdc_disable;	/* PDC RX/TX disable */
+};
+
+
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation.  All fields except dma_intr_handler() are initialized
+ * by the interface.  The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct at32_pcm_dma_params {
+	char *name;		/* stream identifier */
+	int pdc_xfer_size;	/* PDC counter increment in bytes */
+	struct ssc_device *ssc;	/* SSC device for stream */
+	struct at32_pdc_regs *pdc;	/* PDC register info */
+	struct at32_ssc_mask *mask;	/* SSC mask info */
+	struct snd_pcm_substream *substream;
+	void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
+};
+
+
+
+/*
+ * The AT32 ASoC platform driver
+ */
+extern struct snd_soc_platform at32_soc_platform;
+
+
+
+/*
+ * SSC register access (since ssc_writel() / ssc_readl() require literal name)
+ */
+#define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
+#define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
+
+#endif /* __SOUND_SOC_AT32_AT32_PCM_H */
diff --git a/sound/soc/at32/at32-ssc.c b/sound/soc/at32/at32-ssc.c
new file mode 100644
index 0000000000000000000000000000000000000000..4ef6492c902ef551be63bb83f75c0e01939c49bd
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.c
@@ -0,0 +1,849 @@
+/* sound/soc/at32/at32-ssc.c
+ * ASoC platform driver for AT32 using SSC as DAI
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.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.
+ *
+ * Note that this is basically a port of the sound/soc/at91-ssc.c to
+ * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
+ */
+
+/* #define DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Constants
+\*-------------------------------------------------------------------------*/
+#define NUM_SSC_DEVICES		3
+
+/*
+ * SSC direction masks
+ */
+#define SSC_DIR_MASK_UNUSED	0
+#define SSC_DIR_MASK_PLAYBACK	1
+#define SSC_DIR_MASK_CAPTURE	2
+
+/*
+ * SSC register values that Atmel left out of <linux/atmel-ssc.h>.  These
+ * are expected to be used with SSC_BF
+ */
+/* START bit field values */
+#define SSC_START_CONTINUOUS	0
+#define SSC_START_TX_RX		1
+#define SSC_START_LOW_RF	2
+#define SSC_START_HIGH_RF	3
+#define SSC_START_FALLING_RF	4
+#define SSC_START_RISING_RF	5
+#define SSC_START_LEVEL_RF	6
+#define SSC_START_EDGE_RF	7
+#define SSS_START_COMPARE_0	8
+
+/* CKI bit field values */
+#define SSC_CKI_FALLING		0
+#define SSC_CKI_RISING		1
+
+/* CKO bit field values */
+#define SSC_CKO_NONE		0
+#define SSC_CKO_CONTINUOUS	1
+#define SSC_CKO_TRANSFER	2
+
+/* CKS bit field values */
+#define SSC_CKS_DIV		0
+#define SSC_CKS_CLOCK		1
+#define SSC_CKS_PIN		2
+
+/* FSEDGE bit field values */
+#define SSC_FSEDGE_POSITIVE	0
+#define SSC_FSEDGE_NEGATIVE	1
+
+/* FSOS bit field values */
+#define SSC_FSOS_NONE		0
+#define SSC_FSOS_NEGATIVE	1
+#define SSC_FSOS_POSITIVE	2
+#define SSC_FSOS_LOW		3
+#define SSC_FSOS_HIGH		4
+#define SSC_FSOS_TOGGLE		5
+
+#define START_DELAY		1
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Module data
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC PDC registered required by the PCM DMA engine
+ */
+static struct at32_pdc_regs pdc_tx_reg = {
+	.xpr = SSC_PDC_TPR,
+	.xcr = SSC_PDC_TCR,
+	.xnpr = SSC_PDC_TNPR,
+	.xncr = SSC_PDC_TNCR,
+};
+
+
+
+static struct at32_pdc_regs pdc_rx_reg = {
+	.xpr = SSC_PDC_RPR,
+	.xcr = SSC_PDC_RCR,
+	.xnpr = SSC_PDC_RNPR,
+	.xncr = SSC_PDC_RNCR,
+};
+
+
+
+/*
+ * SSC and PDC status bits for transmit and receive
+ */
+static struct at32_ssc_mask ssc_tx_mask = {
+	.ssc_enable = SSC_BIT(CR_TXEN),
+	.ssc_disable = SSC_BIT(CR_TXDIS),
+	.ssc_endx = SSC_BIT(SR_ENDTX),
+	.ssc_endbuf = SSC_BIT(SR_TXBUFE),
+	.pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
+	.pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
+};
+
+
+
+static struct at32_ssc_mask ssc_rx_mask = {
+	.ssc_enable = SSC_BIT(CR_RXEN),
+	.ssc_disable = SSC_BIT(CR_RXDIS),
+	.ssc_endx = SSC_BIT(SR_ENDRX),
+	.ssc_endbuf = SSC_BIT(SR_RXBUFF),
+	.pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
+	.pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
+};
+
+
+
+/*
+ * DMA parameters for each SSC
+ */
+static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+	{
+	 {
+	  .name = "SSC0 PCM out",
+	  .pdc = &pdc_tx_reg,
+	  .mask = &ssc_tx_mask,
+	  },
+	 {
+	  .name = "SSC0 PCM in",
+	  .pdc = &pdc_rx_reg,
+	  .mask = &ssc_rx_mask,
+	  },
+	 },
+	{
+	 {
+	  .name = "SSC1 PCM out",
+	  .pdc = &pdc_tx_reg,
+	  .mask = &ssc_tx_mask,
+	  },
+	 {
+	  .name = "SSC1 PCM in",
+	  .pdc = &pdc_rx_reg,
+	  .mask = &ssc_rx_mask,
+	  },
+	 },
+	{
+	 {
+	  .name = "SSC2 PCM out",
+	  .pdc = &pdc_tx_reg,
+	  .mask = &ssc_tx_mask,
+	  },
+	 {
+	  .name = "SSC2 PCM in",
+	  .pdc = &pdc_rx_reg,
+	  .mask = &ssc_rx_mask,
+	  },
+	 },
+};
+
+
+
+static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
+	{
+	 .name = "ssc0",
+	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
+	 .dir_mask = SSC_DIR_MASK_UNUSED,
+	 .initialized = 0,
+	 },
+	{
+	 .name = "ssc1",
+	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
+	 .dir_mask = SSC_DIR_MASK_UNUSED,
+	 .initialized = 0,
+	 },
+	{
+	 .name = "ssc2",
+	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
+	 .dir_mask = SSC_DIR_MASK_UNUSED,
+	 .initialized = 0,
+	 },
+};
+
+
+
+
+/*-------------------------------------------------------------------------*\
+ * ISR
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC interrupt handler.  Passes PDC interrupts to the DMA interrupt
+ * handler in the PCM driver.
+ */
+static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
+{
+	struct at32_ssc_info *ssc_p = dev_id;
+	struct at32_pcm_dma_params *dma_params;
+	u32 ssc_sr;
+	u32 ssc_substream_mask;
+	int i;
+
+	ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
+		  ssc_readl(ssc_p->ssc->regs, IMR));
+
+	/*
+	 * Loop through substreams attached to this SSC.  If a DMA-related
+	 * interrupt occured on that substream, call the DMA interrupt
+	 * handler function, if one has been registered in the dma_param
+	 * structure by the PCM driver.
+	 */
+	for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+		dma_params = ssc_p->dma_params[i];
+
+		if ((dma_params != NULL) &&
+		    (dma_params->dma_intr_handler != NULL)) {
+			ssc_substream_mask = (dma_params->mask->ssc_endx |
+					      dma_params->mask->ssc_endbuf);
+			if (ssc_sr & ssc_substream_mask) {
+				dma_params->dma_intr_handler(ssc_sr,
+							     dma_params->
+							     substream);
+			}
+		}
+	}
+
+
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*\
+ * DAI functions
+\*-------------------------------------------------------------------------*/
+/*
+ * Startup.  Only that one substream allowed in each direction.
+ */
+static int at32_ssc_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	int dir_mask;
+
+	dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		    SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
+
+	spin_lock_irq(&ssc_p->lock);
+	if (ssc_p->dir_mask & dir_mask) {
+		spin_unlock_irq(&ssc_p->lock);
+		return -EBUSY;
+	}
+	ssc_p->dir_mask |= dir_mask;
+	spin_unlock_irq(&ssc_p->lock);
+
+	return 0;
+}
+
+
+
+/*
+ * Shutdown.  Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	struct at32_pcm_dma_params *dma_params;
+	int dir_mask;
+
+	dma_params = ssc_p->dma_params[substream->stream];
+
+	if (dma_params != NULL) {
+		ssc_writel(dma_params->ssc->regs, CR,
+			   dma_params->mask->ssc_disable);
+		pr_debug("%s disabled SSC_SR=0x%08x\n",
+			 (substream->stream ? "receiver" : "transmit"),
+			 ssc_readl(ssc_p->ssc->regs, SR));
+
+		dma_params->ssc = NULL;
+		dma_params->substream = NULL;
+		ssc_p->dma_params[substream->stream] = NULL;
+	}
+
+
+	dir_mask = 1 << substream->stream;
+	spin_lock_irq(&ssc_p->lock);
+	ssc_p->dir_mask &= ~dir_mask;
+	if (!ssc_p->dir_mask) {
+		/* Shutdown the SSC clock */
+		pr_debug("at32-ssc: Stopping user %d clock\n",
+			 ssc_p->ssc->user);
+		clk_disable(ssc_p->ssc->clk);
+
+		if (ssc_p->initialized) {
+			free_irq(ssc_p->ssc->irq, ssc_p);
+			ssc_p->initialized = 0;
+		}
+
+		/* Reset the SSC */
+		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+		/* clear the SSC dividers */
+		ssc_p->cmr_div = 0;
+		ssc_p->tcmr_period = 0;
+		ssc_p->rcmr_period = 0;
+	}
+	spin_unlock_irq(&ssc_p->lock);
+}
+
+
+
+/*
+ * Set the SSC system clock rate
+ */
+static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+				   int clk_id, unsigned int freq, int dir)
+{
+	/* TODO: What the heck do I do here? */
+	return 0;
+}
+
+
+
+/*
+ * Record DAI format for use by hw_params()
+ */
+static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+				unsigned int fmt)
+{
+	struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+	ssc_p->daifmt = fmt;
+	return 0;
+}
+
+
+
+/*
+ * Record SSC clock dividers for use in hw_params()
+ */
+static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+				   int div_id, int div)
+{
+	struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+	switch (div_id) {
+	case AT32_SSC_CMR_DIV:
+		/*
+		 * The same master clock divider is used for both
+		 * transmit and receive, so if a value has already
+		 * been set, it must match this value
+		 */
+		if (ssc_p->cmr_div == 0)
+			ssc_p->cmr_div = div;
+		else if (div != ssc_p->cmr_div)
+			return -EBUSY;
+		break;
+
+	case AT32_SSC_TCMR_PERIOD:
+		ssc_p->tcmr_period = div;
+		break;
+
+	case AT32_SSC_RCMR_PERIOD:
+		ssc_p->rcmr_period = div;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * Configure the SSC
+ */
+static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int id = rtd->dai->cpu_dai->id;
+	struct at32_ssc_info *ssc_p = &ssc_info[id];
+	struct at32_pcm_dma_params *dma_params;
+	int channels, bits;
+	u32 tfmr, rfmr, tcmr, rcmr;
+	int start_event;
+	int ret;
+
+
+	/*
+	 * Currently, there is only one set of dma_params for each direction.
+	 * If more are added, this code will have to be changed to select
+	 * the proper set
+	 */
+	dma_params = &ssc_dma_params[id][substream->stream];
+	dma_params->ssc = ssc_p->ssc;
+	dma_params->substream = substream;
+
+	ssc_p->dma_params[substream->stream] = dma_params;
+
+
+	/*
+	 * The cpu_dai->dma_data field is only used to communicate the
+	 * appropriate DMA parameters to the PCM driver's hw_params()
+	 * function.  It should not be used for other purposes as it
+	 * is common to all substreams.
+	 */
+	rtd->dai->cpu_dai->dma_data = dma_params;
+
+	channels = params_channels(params);
+
+
+	/*
+	 * Determine sample size in bits and the PDC increment
+	 */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		bits = 8;
+		dma_params->pdc_xfer_size = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16:
+		bits = 16;
+		dma_params->pdc_xfer_size = 2;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24:
+		bits = 24;
+		dma_params->pdc_xfer_size = 4;
+		break;
+
+	case SNDRV_PCM_FORMAT_S32:
+		bits = 32;
+		dma_params->pdc_xfer_size = 4;
+		break;
+
+	default:
+		pr_warning("at32-ssc: Unsupported PCM format %d",
+			   params_format(params));
+		return -EINVAL;
+	}
+	pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
+		 bits, dma_params->pdc_xfer_size, channels);
+
+
+	/*
+	 * The SSC only supports up to 16-bit samples in I2S format, due
+	 * to the size of the Frame Mode Register FSLEN field.
+	 */
+	if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+		if (bits > 16) {
+			pr_warning("at32-ssc: "
+				   "sample size %d is too large for I2S\n",
+				   bits);
+			return -EINVAL;
+		}
+
+
+	/*
+	 * Compute the SSC register settings
+	 */
+	switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+				 SND_SOC_DAIFMT_MASTER_MASK)) {
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+		/*
+		 * I2S format, SSC provides BCLK and LRS clocks.
+		 *
+		 * The SSC transmit and receive clocks are generated from the
+		 * MCK divider, and the BCLK signal is output on the SSC TK line
+		 */
+		pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
+		rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+			SSC_BF(RCMR_STTDLY, START_DELAY) |
+			SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
+			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
+			SSC_BF(RFMR_FSLEN, bits - 1) |
+			SSC_BF(RFMR_DATNB, channels - 1) |
+			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+		tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+			SSC_BF(TCMR_STTDLY, START_DELAY) |
+			SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
+			SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+			SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+			SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
+			SSC_BF(TFMR_FSLEN, bits - 1) |
+			SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
+			SSC_BF(TFMR_DATLEN, bits - 1));
+		break;
+
+
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+		/*
+		 * I2S format, CODEC supplies BCLK and LRC clock.
+		 *
+		 * The SSC transmit clock is obtained from the BCLK signal
+		 * on the TK line, and the SSC receive clock is generated from
+		 * the transmit clock.
+		 *
+		 * For single channel data, one sample is transferred on the
+		 * falling edge of the LRC clock.  For two channel data, one
+		 * sample is transferred on both edges of the LRC clock.
+		 */
+		pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
+		start_event = ((channels == 1) ?
+			       SSC_START_FALLING_RF : SSC_START_EDGE_RF);
+
+		rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
+			SSC_BF(RCMR_START, start_event) |
+			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
+
+		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
+			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+		tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
+			SSC_BF(TCMR_START, start_event) |
+			SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+			SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(TCMR_CKS, SSC_CKS_PIN));
+
+		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
+			SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+		break;
+
+
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+		/*
+		 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+		 *
+		 * The SSC transmit and receive clocks are generated from the
+		 * MCK divider, and the BCLK signal is output on the SSC TK line
+		 */
+		pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
+		rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+			SSC_BF(RCMR_STTDLY, 1) |
+			SSC_BF(RCMR_START, SSC_START_RISING_RF) |
+			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
+			SSC_BF(RFMR_DATNB, channels - 1) |
+			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+		tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+			SSC_BF(TCMR_STTDLY, 1) |
+			SSC_BF(TCMR_START, SSC_START_RISING_RF) |
+			SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+			SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
+			SSC_BF(TFMR_DATNB, channels - 1) |
+			SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+		break;
+
+
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+	default:
+		pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
+			   ssc_p->daifmt);
+		return -EINVAL;
+		break;
+	}
+	pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
+		 rcmr, rfmr, tcmr, tfmr);
+
+
+	if (!ssc_p->initialized) {
+		/* enable peripheral clock */
+		pr_debug("at32-ssc: Starting clock\n");
+		clk_enable(ssc_p->ssc->clk);
+
+		/* Reset the SSC and its PDC registers */
+		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+		ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
+
+		ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
+
+		ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
+				  ssc_p->name, ssc_p);
+		if (ret < 0) {
+			pr_warning("at32-ssc: request irq failed (%d)\n", ret);
+			pr_debug("at32-ssc: Stopping clock\n");
+			clk_disable(ssc_p->ssc->clk);
+			return ret;
+		}
+
+		ssc_p->initialized = 1;
+	}
+
+	/* Set SSC clock mode register */
+	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+
+	/* set receive clock mode and format */
+	ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
+	ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
+
+	/* set transmit clock mode and format */
+	ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
+	ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
+
+	pr_debug("at32-ssc: SSC initialized\n");
+	return 0;
+}
+
+
+
+static int at32_ssc_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	struct at32_pcm_dma_params *dma_params;
+
+	dma_params = ssc_p->dma_params[substream->stream];
+
+	ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
+
+	return 0;
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_ssc_suspend(struct platform_device *pdev,
+			    struct snd_soc_dai *cpu_dai)
+{
+	struct at32_ssc_info *ssc_p;
+
+	if (!cpu_dai->active)
+		return 0;
+
+	ssc_p = &ssc_info[cpu_dai->id];
+
+	/* Save the status register before disabling transmit and receive */
+	ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
+	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
+
+	/* Save the current interrupt mask, then disable unmasked interrupts */
+	ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
+	ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
+
+	ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
+	ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
+	ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
+	ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
+	ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+
+	return 0;
+}
+
+
+
+static int at32_ssc_resume(struct platform_device *pdev,
+			   struct snd_soc_dai *cpu_dai)
+{
+	struct at32_ssc_info *ssc_p;
+	u32 cr;
+
+	if (!cpu_dai->active)
+		return 0;
+
+	ssc_p = &ssc_info[cpu_dai->id];
+
+	/* restore SSC register settings */
+	ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
+	ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
+	ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
+	ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
+	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
+
+	/* re-enable interrupts */
+	ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
+
+	/* Re-enable recieve and transmit as appropriate */
+	cr = 0;
+	cr |=
+	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
+	cr |=
+	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
+	ssc_writel(ssc_p->ssc->regs, CR, cr);
+
+	return 0;
+}
+#else /* CONFIG_PM */
+#  define at32_ssc_suspend	NULL
+#  define at32_ssc_resume	NULL
+#endif /* CONFIG_PM */
+
+
+#define AT32_SSC_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 AT32_SSC_FORMATS \
+    (SNDRV_PCM_FMTBIT_S8  | SNDRV_PCM_FMTBIT_S16 | \
+     SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
+
+
+struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
+	{
+	 .name = "at32-ssc0",
+	 .id = 0,
+	 .type = SND_SOC_DAI_PCM,
+	 .suspend = at32_ssc_suspend,
+	 .resume = at32_ssc_resume,
+	 .playback = {
+		      .channels_min = 1,
+		      .channels_max = 2,
+		      .rates = AT32_SSC_RATES,
+		      .formats = AT32_SSC_FORMATS,
+		      },
+	 .capture = {
+		     .channels_min = 1,
+		     .channels_max = 2,
+		     .rates = AT32_SSC_RATES,
+		     .formats = AT32_SSC_FORMATS,
+		     },
+	 .ops = {
+		 .startup = at32_ssc_startup,
+		 .shutdown = at32_ssc_shutdown,
+		 .prepare = at32_ssc_prepare,
+		 .hw_params = at32_ssc_hw_params,
+		 },
+	 .dai_ops = {
+		     .set_sysclk = at32_ssc_set_dai_sysclk,
+		     .set_fmt = at32_ssc_set_dai_fmt,
+		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
+		     },
+	 .private_data = &ssc_info[0],
+	 },
+	{
+	 .name = "at32-ssc1",
+	 .id = 1,
+	 .type = SND_SOC_DAI_PCM,
+	 .suspend = at32_ssc_suspend,
+	 .resume = at32_ssc_resume,
+	 .playback = {
+		      .channels_min = 1,
+		      .channels_max = 2,
+		      .rates = AT32_SSC_RATES,
+		      .formats = AT32_SSC_FORMATS,
+		      },
+	 .capture = {
+		     .channels_min = 1,
+		     .channels_max = 2,
+		     .rates = AT32_SSC_RATES,
+		     .formats = AT32_SSC_FORMATS,
+		     },
+	 .ops = {
+		 .startup = at32_ssc_startup,
+		 .shutdown = at32_ssc_shutdown,
+		 .prepare = at32_ssc_prepare,
+		 .hw_params = at32_ssc_hw_params,
+		 },
+	 .dai_ops = {
+		     .set_sysclk = at32_ssc_set_dai_sysclk,
+		     .set_fmt = at32_ssc_set_dai_fmt,
+		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
+		     },
+	 .private_data = &ssc_info[1],
+	 },
+	{
+	 .name = "at32-ssc2",
+	 .id = 2,
+	 .type = SND_SOC_DAI_PCM,
+	 .suspend = at32_ssc_suspend,
+	 .resume = at32_ssc_resume,
+	 .playback = {
+		      .channels_min = 1,
+		      .channels_max = 2,
+		      .rates = AT32_SSC_RATES,
+		      .formats = AT32_SSC_FORMATS,
+		      },
+	 .capture = {
+		     .channels_min = 1,
+		     .channels_max = 2,
+		     .rates = AT32_SSC_RATES,
+		     .formats = AT32_SSC_FORMATS,
+		     },
+	 .ops = {
+		 .startup = at32_ssc_startup,
+		 .shutdown = at32_ssc_shutdown,
+		 .prepare = at32_ssc_prepare,
+		 .hw_params = at32_ssc_hw_params,
+		 },
+	 .dai_ops = {
+		     .set_sysclk = at32_ssc_set_dai_sysclk,
+		     .set_fmt = at32_ssc_set_dai_fmt,
+		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
+		     },
+	 .private_data = &ssc_info[2],
+	 },
+};
+EXPORT_SYMBOL_GPL(at32_ssc_dai);
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-ssc.h b/sound/soc/at32/at32-ssc.h
new file mode 100644
index 0000000000000000000000000000000000000000..3c052dbbe460f9509af4c674a39010ddb74b4301
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.h
@@ -0,0 +1,59 @@
+/* sound/soc/at32/at32-ssc.h
+ * ASoC SSC interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.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.
+ */
+
+#ifndef __SOUND_SOC_AT32_AT32_SSC_H
+#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
+
+#include <linux/types.h>
+#include <linux/atmel-ssc.h>
+
+#include "at32-pcm.h"
+
+
+
+struct at32_ssc_state {
+	u32 ssc_cmr;
+	u32 ssc_rcmr;
+	u32 ssc_rfmr;
+	u32 ssc_tcmr;
+	u32 ssc_tfmr;
+	u32 ssc_sr;
+	u32 ssc_imr;
+};
+
+
+
+struct at32_ssc_info {
+	char *name;
+	struct ssc_device *ssc;
+	spinlock_t lock;	/* lock for dir_mask */
+	unsigned short dir_mask;	/* 0=unused, 1=playback, 2=capture */
+	unsigned short initialized;	/* true if SSC has been initialized */
+	unsigned short daifmt;
+	unsigned short cmr_div;
+	unsigned short tcmr_period;
+	unsigned short rcmr_period;
+	struct at32_pcm_dma_params *dma_params[2];
+	struct at32_ssc_state ssc_state;
+};
+
+
+/* SSC divider ids */
+#define AT32_SSC_CMR_DIV        0	/* MCK divider for BCLK */
+#define AT32_SSC_TCMR_PERIOD    1	/* BCLK divider for transmit FS */
+#define AT32_SSC_RCMR_PERIOD    2	/* BCLK divider for receive FS */
+
+
+extern struct snd_soc_dai at32_ssc_dai[];
+
+
+
+#endif /* __SOUND_SOC_AT32_AT32_SSC_H */
diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c
new file mode 100644
index 0000000000000000000000000000000000000000..fee5f8e589575ab783a44c5366d1438c4e50bb28
--- /dev/null
+++ b/sound/soc/at32/playpaq_wm8510.c
@@ -0,0 +1,522 @@
+/* sound/soc/at32/playpaq_wm8510.c
+ * ASoC machine driver for PlayPaq using WM8510 codec
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.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 code is largely inspired by sound/soc/at91/eti_b1_wm8731.c
+ *
+ * NOTE: If you don't have the AT32 enhanced portmux configured (which
+ * isn't currently in the mainline or Atmel patched kernel), you will
+ * need to set the MCLK pin (PA30) to peripheral A in your board initialization
+ * code.  Something like:
+ *	at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.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 <asm/arch/at32ap700x.h>
+#include <asm/arch/portmux.h>
+
+#include "../codecs/wm8510.h"
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+/*-------------------------------------------------------------------------*\
+ * constants
+\*-------------------------------------------------------------------------*/
+#define MCLK_PIN		GPIO_PIN_PA(30)
+#define MCLK_PERIPH		GPIO_PERIPH_A
+
+
+/*-------------------------------------------------------------------------*\
+ * data types
+\*-------------------------------------------------------------------------*/
+/* SSC clocking data */
+struct ssc_clock_data {
+	/* CMR div */
+	unsigned int cmr_div;
+
+	/* Frame period (as needed by xCMR.PERIOD) */
+	unsigned int period;
+
+	/* The SSC clock rate these settings where calculated for */
+	unsigned long ssc_rate;
+};
+
+
+/*-------------------------------------------------------------------------*\
+ * module data
+\*-------------------------------------------------------------------------*/
+static struct clk *_gclk0;
+static struct clk *_pll0;
+
+#define CODEC_CLK (_gclk0)
+
+
+/*-------------------------------------------------------------------------*\
+ * Sound SOC operations
+\*-------------------------------------------------------------------------*/
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *cpu_dai)
+{
+	struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+	struct ssc_device *ssc = ssc_p->ssc;
+	struct ssc_clock_data cd;
+	unsigned int rate, width_bits, channels;
+	unsigned int bitrate, ssc_div;
+	unsigned actual_rate;
+
+
+	/*
+	 * Figure out required bitrate
+	 */
+	rate = params_rate(params);
+	channels = params_channels(params);
+	width_bits = snd_pcm_format_physical_width(params_format(params));
+	bitrate = rate * width_bits * channels;
+
+
+	/*
+	 * Figure out required SSC divider and period for required bitrate
+	 */
+	cd.ssc_rate = clk_get_rate(ssc->clk);
+	ssc_div = cd.ssc_rate / bitrate;
+	cd.cmr_div = ssc_div / 2;
+	if (ssc_div & 1) {
+		/* round cmr_div up */
+		cd.cmr_div++;
+	}
+	cd.period = width_bits - 1;
+
+
+	/*
+	 * Find actual rate, compare to requested rate
+	 */
+	actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
+	pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
+		 rate, actual_rate);
+
+
+	return cd;
+}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+
+static int playpaq_wm8510_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;
+	struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+	struct ssc_device *ssc = ssc_p->ssc;
+	unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
+	int ret;
+
+
+	/* Due to difficulties with getting the correct clocks from the AT32's
+	 * PLL0, we're going to let the CODEC be in charge of all the clocks
+	 */
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+	const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+#else
+	struct ssc_clock_data cd;
+	const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+#endif
+
+	if (ssc == NULL) {
+		pr_warning("playpaq_wm8510_hw_params: ssc is NULL!\n");
+		return -EINVAL;
+	}
+
+
+	/*
+	 * Figure out PLL and BCLK dividers for WM8510
+	 */
+	switch (params_rate(params)) {
+	case 48000:
+		pll_out = 12288000;
+		mclk_div = WM8510_MCLKDIV_1;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 44100:
+		pll_out = 11289600;
+		mclk_div = WM8510_MCLKDIV_1;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 22050:
+		pll_out = 11289600;
+		mclk_div = WM8510_MCLKDIV_2;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 16000:
+		pll_out = 12288000;
+		mclk_div = WM8510_MCLKDIV_3;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 11025:
+		pll_out = 11289600;
+		mclk_div = WM8510_MCLKDIV_4;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 8000:
+		pll_out = 12288000;
+		mclk_div = WM8510_MCLKDIV_6;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	default:
+		pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
+			   params_rate(params));
+		return -EINVAL;
+	}
+
+
+	/*
+	 * set CPU and CODEC DAI configuration
+	 */
+	ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: "
+			   "Failed to set CODEC DAI format (%d)\n",
+			   ret);
+		return ret;
+	}
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: "
+			   "Failed to set CPU DAI format (%d)\n",
+			   ret);
+		return ret;
+	}
+
+
+	/*
+	 * Set CPU clock configuration
+	 */
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+	cd = playpaq_wm8510_calc_ssc_clock(params, cpu_dai);
+	pr_debug("playpaq_wm8510: cmr_div = %d, period = %d\n",
+		 cd.cmr_div, cd.period);
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_CMR_DIV, cd.cmr_div);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: Failed to set CPU CMR_DIV (%d)\n",
+			   ret);
+		return ret;
+	}
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_TCMR_PERIOD,
+					  cd.period);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: "
+			   "Failed to set CPU transmit period (%d)\n",
+			   ret);
+		return ret;
+	}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+	/*
+	 * Set CODEC clock configuration
+	 */
+	pr_debug("playpaq_wm8510: "
+		 "pll_in = %ld, pll_out = %u, bclk = %x, mclk = %x\n",
+		 clk_get_rate(CODEC_CLK), pll_out, bclk, mclk_div);
+
+
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_BCLKDIV, bclk);
+	if (ret < 0) {
+		pr_warning
+		    ("playpaq_wm8510: Failed to set CODEC DAI BCLKDIV (%d)\n",
+		     ret);
+		return ret;
+	}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+	ret = snd_soc_dai_set_pll(codec_dai, 0,
+					 clk_get_rate(CODEC_CLK), pll_out);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
+			   ret);
+		return ret;
+	}
+
+
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_MCLKDIV, mclk_div);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: Failed to set CODEC MCLKDIV (%d)\n",
+			   ret);
+		return ret;
+	}
+
+
+	return 0;
+}
+
+
+
+static struct snd_soc_ops playpaq_wm8510_ops = {
+	.hw_params = playpaq_wm8510_hw_params,
+};
+
+
+
+static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("Int Mic", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+
+
+static const char *intercon[][3] = {
+	/* speaker connected to SPKOUT */
+	{"Ext Spk", NULL, "SPKOUTP"},
+	{"Ext Spk", NULL, "SPKOUTN"},
+
+	{"Mic Bias", NULL, "Int Mic"},
+	{"MICN", NULL, "Mic Bias"},
+	{"MICP", NULL, "Mic Bias"},
+
+	/* Terminator */
+	{NULL, NULL, NULL},
+};
+
+
+
+static int playpaq_wm8510_init(struct snd_soc_codec *codec)
+{
+	int i;
+
+	/*
+	 * Add DAPM widgets
+	 */
+	for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
+		snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]);
+
+
+
+	/*
+	 * Setup audio path interconnects
+	 */
+	for (i = 0; intercon[i][0] != NULL; i++) {
+		snd_soc_dapm_connect_input(codec,
+					   intercon[i][0],
+					   intercon[i][1], intercon[i][2]);
+	}
+
+
+	/* always connected pins */
+	snd_soc_dapm_enable_pin(codec, "Int Mic");
+	snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	snd_soc_dapm_sync(codec);
+
+
+
+	/* Make CSB show PLL rate */
+	snd_soc_dai_set_clkdiv(codec->dai, WM8510_OPCLKDIV,
+				       WM8510_OPCLKDIV_1 | 4);
+
+	return 0;
+}
+
+
+
+static struct snd_soc_dai_link playpaq_wm8510_dai = {
+	.name = "WM8510",
+	.stream_name = "WM8510 PCM",
+	.cpu_dai = &at32_ssc_dai[0],
+	.codec_dai = &wm8510_dai,
+	.init = playpaq_wm8510_init,
+	.ops = &playpaq_wm8510_ops,
+};
+
+
+
+static struct snd_soc_machine snd_soc_machine_playpaq = {
+	.name = "LRS_PlayPaq_WM8510",
+	.dai_link = &playpaq_wm8510_dai,
+	.num_links = 1,
+};
+
+
+
+static struct wm8510_setup_data playpaq_wm8510_setup = {
+	.i2c_address = 0x1a,
+};
+
+
+
+static struct snd_soc_device playpaq_wm8510_snd_devdata = {
+	.machine = &snd_soc_machine_playpaq,
+	.platform = &at32_soc_platform,
+	.codec_dev = &soc_codec_dev_wm8510,
+	.codec_data = &playpaq_wm8510_setup,
+};
+
+static struct platform_device *playpaq_snd_device;
+
+
+static int __init playpaq_asoc_init(void)
+{
+	int ret = 0;
+	struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+	struct ssc_device *ssc = NULL;
+
+
+	/*
+	 * Request SSC device
+	 */
+	ssc = ssc_request(0);
+	if (IS_ERR(ssc)) {
+		ret = PTR_ERR(ssc);
+		ssc = NULL;
+		goto err_ssc;
+	}
+	ssc_p->ssc = ssc;
+
+
+	/*
+	 * Configure MCLK for WM8510
+	 */
+	_gclk0 = clk_get(NULL, "gclk0");
+	if (IS_ERR(_gclk0)) {
+		_gclk0 = NULL;
+		goto err_gclk0;
+	}
+	_pll0 = clk_get(NULL, "pll0");
+	if (IS_ERR(_pll0)) {
+		_pll0 = NULL;
+		goto err_pll0;
+	}
+	if (clk_set_parent(_gclk0, _pll0)) {
+		pr_warning("snd-soc-playpaq: "
+			   "Failed to set PLL0 as parent for DAC clock\n");
+		goto err_set_clk;
+	}
+	clk_set_rate(CODEC_CLK, 12000000);
+	clk_enable(CODEC_CLK);
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+	at32_select_periph(MCLK_PIN, MCLK_PERIPH, 0);
+#endif
+
+
+	/*
+	 * Create and register platform device
+	 */
+	playpaq_snd_device = platform_device_alloc("soc-audio", 0);
+	if (playpaq_snd_device == NULL) {
+		ret = -ENOMEM;
+		goto err_device_alloc;
+	}
+
+	platform_set_drvdata(playpaq_snd_device, &playpaq_wm8510_snd_devdata);
+	playpaq_wm8510_snd_devdata.dev = &playpaq_snd_device->dev;
+
+	ret = platform_device_add(playpaq_snd_device);
+	if (ret) {
+		pr_warning("playpaq_wm8510: platform_device_add failed (%d)\n",
+			   ret);
+		goto err_device_add;
+	}
+
+	return 0;
+
+
+err_device_add:
+	if (playpaq_snd_device != NULL) {
+		platform_device_put(playpaq_snd_device);
+		playpaq_snd_device = NULL;
+	}
+err_device_alloc:
+err_set_clk:
+	if (_pll0 != NULL) {
+		clk_put(_pll0);
+		_pll0 = NULL;
+	}
+err_pll0:
+	if (_gclk0 != NULL) {
+		clk_put(_gclk0);
+		_gclk0 = NULL;
+	}
+err_gclk0:
+	if (ssc != NULL) {
+		ssc_free(ssc);
+		ssc = NULL;
+	}
+err_ssc:
+	return ret;
+}
+
+
+static void __exit playpaq_asoc_exit(void)
+{
+	struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+	struct ssc_device *ssc;
+
+	if (ssc_p != NULL) {
+		ssc = ssc_p->ssc;
+		if (ssc != NULL)
+			ssc_free(ssc);
+		ssc_p->ssc = NULL;
+	}
+
+	if (_gclk0 != NULL) {
+		clk_put(_gclk0);
+		_gclk0 = NULL;
+	}
+	if (_pll0 != NULL) {
+		clk_put(_pll0);
+		_pll0 = NULL;
+	}
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+	at32_free_pin(MCLK_PIN);
+#endif
+
+	platform_device_unregister(playpaq_snd_device);
+	playpaq_snd_device = NULL;
+}
+
+module_init(playpaq_asoc_init);
+module_exit(playpaq_asoc_exit);
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("ASoC machine driver for LRS PlayPaq");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
index 5cb93fd3a4071d477cd38bab269b2bbfe2fda50d..905186502e0009039efc81919022b9d4ffe44a54 100644
--- a/sound/soc/at91/Kconfig
+++ b/sound/soc/at91/Kconfig
@@ -1,6 +1,6 @@
 config SND_AT91_SOC
 	tristate "SoC Audio for the Atmel AT91 System-on-Chip"
-	depends on ARCH_AT91 && SND_SOC
+	depends on ARCH_AT91
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the AT91 SSC interface. You will also need
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
index ccac6bd2889c0f56589f2234aa899aeb42b42ff0..d47492b2b6e5db1494e466de57da06248cbc2ac0 100644
--- a/sound/soc/at91/at91-pcm.c
+++ b/sound/soc/at91/at91-pcm.c
@@ -318,7 +318,7 @@ static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
 static u64 at91_pcm_dmamask = 0xffffffff;
 
 static int at91_pcm_new(struct snd_card *card,
-	struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+	struct snd_soc_dai *dai, struct snd_pcm *pcm)
 {
 	int ret = 0;
 
@@ -367,7 +367,7 @@ static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
 #ifdef CONFIG_PM
 static int at91_pcm_suspend(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	struct snd_pcm_runtime *runtime = dai->runtime;
 	struct at91_runtime_data *prtd;
@@ -392,7 +392,7 @@ static int at91_pcm_suspend(struct platform_device *pdev,
 }
 
 static int at91_pcm_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	struct snd_pcm_runtime *runtime = dai->runtime;
 	struct at91_runtime_data *prtd;
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
index bc35d00a38f86b89778bd86953a165d4075dfef2..c3625b665c5a027ee0ed06f7f8f6582da3a026a2 100644
--- a/sound/soc/at91/at91-ssc.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -281,7 +281,7 @@ static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
 /*
  * Record the SSC system clock rate.
  */
-static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	/*
@@ -303,7 +303,7 @@ static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
 /*
  * Record the DAI format for use in hw_params().
  */
-static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
 	struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -315,7 +315,7 @@ static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
 /*
  * Record SSC clock dividers for use in hw_params().
  */
-static int at91_ssc_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
 	int div_id, int div)
 {
 	struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -634,7 +634,7 @@ static int at91_ssc_prepare(struct snd_pcm_substream *substream)
 
 #ifdef CONFIG_PM
 static int at91_ssc_suspend(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *cpu_dai)
+	struct snd_soc_dai *cpu_dai)
 {
 	struct at91_ssc_info *ssc_p;
 
@@ -662,7 +662,7 @@ static int at91_ssc_suspend(struct platform_device *pdev,
 }
 
 static int at91_ssc_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *cpu_dai)
+	struct snd_soc_dai *cpu_dai)
 {
 	struct at91_ssc_info *ssc_p;
 
@@ -700,7 +700,7 @@ static int at91_ssc_resume(struct platform_device *pdev,
 #define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
 			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
-struct snd_soc_cpu_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
+struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
 	{	.name = "at91-ssc0",
 		.id = 0,
 		.type = SND_SOC_DAI_PCM,
diff --git a/sound/soc/at91/at91-ssc.h b/sound/soc/at91/at91-ssc.h
index b188f973df9fea1d0554bf36b26b4ad913649674..6b7bf382d06f78ddbaf2435c35f32a330b66f534 100644
--- a/sound/soc/at91/at91-ssc.h
+++ b/sound/soc/at91/at91-ssc.h
@@ -21,7 +21,7 @@
 #define AT91SSC_TCMR_PERIOD	1 /* BCLK divider for transmit FS */
 #define AT91SSC_RCMR_PERIOD	2 /* BCLK divider for receive FS */
 
-extern struct snd_soc_cpu_dai at91_ssc_dai[];
+extern struct snd_soc_dai at91_ssc_dai[];
 
 #endif /* _AT91_SSC_H */
 
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
index 1347dcf3f80b822a300e522d93abbdd2e6ea3202..d532de954241d6bda834fb9df868f411f4bf0df6 100644
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ b/sound/soc/at91/eti_b1_wm8731.c
@@ -53,18 +53,18 @@ static struct clk *pllb_clk;
 static int eti_b1_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int ret;
 
 	/* cpu clock is the AT91 master clock sent to the SSC */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
 		60000000, SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* codec system clock is supplied by PCK1, set to 12MHz */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
 		12000000, SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -87,8 +87,8 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int ret;
 
 #ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
@@ -96,13 +96,13 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
 	int cmr_div, period;
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
@@ -141,17 +141,17 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	/* set the MCK divider for BCLK */
-	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
 	if (ret < 0)
 		return ret;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		/* set the BCLK divider for DACLRC */
-		ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
 						AT91SSC_TCMR_PERIOD, period);
 	} else {
 		/* set the BCLK divider for ADCLRC */
-		ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
 						AT91SSC_RCMR_PERIOD, period);
 	}
 	if (ret < 0)
@@ -163,13 +163,13 @@ static int eti_b1_hw_params(struct snd_pcm_substream *substream,
 	 */
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_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 |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
@@ -191,7 +191,7 @@ static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = {
 	SND_SOC_DAPM_SPK("Ext Spk", NULL),
 };
 
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
 
 	/* speaker connected to LHPOUT */
 	{"Ext Spk", NULL, "LHPOUT"},
@@ -199,9 +199,6 @@ static const char *intercon[][3] = {
 	/* mic is connected to Mic Jack, with WM8731 Mic Bias */
 	{"MICIN", NULL, "Mic Bias"},
 	{"Mic Bias", NULL, "Int Mic"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 /*
@@ -209,30 +206,24 @@ static const char *intercon[][3] = {
  */
 static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
 {
-	int i;
-
 	DBG("eti_b1_wm8731_init() called\n");
 
 	/* Add specific widgets */
-	for(i = 0; i < ARRAY_SIZE(eti_b1_dapm_widgets); i++) {
-		snd_soc_dapm_new_control(codec, &eti_b1_dapm_widgets[i]);
-	}
+	snd_soc_dapm_new_controls(codec, eti_b1_dapm_widgets,
+				  ARRAY_SIZE(eti_b1_dapm_widgets));
 
 	/* Set up specific audio path interconnects */
-	for(i = 0; intercon[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, intercon[i][0],
-			intercon[i][1], intercon[i][2]);
-	}
+	snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon));
 
 	/* not connected */
-	snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
+	snd_soc_dapm_disable_pin(codec, "RLINEIN");
+	snd_soc_dapm_disable_pin(codec, "LLINEIN");
 
 	/* always connected */
-	snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+	snd_soc_dapm_enable_pin(codec, "Int Mic");
+	snd_soc_dapm_enable_pin(codec, "Ext Spk");
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..410a893aa66be2252273cf17864a029675d72944
--- /dev/null
+++ b/sound/soc/au1x/Kconfig
@@ -0,0 +1,32 @@
+##
+## Au1200/Au1550 PSC + DBDMA
+##
+config SND_SOC_AU1XPSC
+	tristate "SoC Audio for Au1200/Au1250/Au1550"
+	depends on SOC_AU1200 || SOC_AU1550
+	help
+	  This option enables support for the Programmable Serial
+	  Controllers in AC97 and I2S mode, and the Descriptor-Based DMA
+	  Controller (DBDMA) as found on the Au1200/Au1250/Au1550 SoC.
+
+config SND_SOC_AU1XPSC_I2S
+	tristate
+
+config SND_SOC_AU1XPSC_AC97
+	tristate
+	select AC97_BUS
+	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_SAMPLE_PSC_AC97
+	tristate "Sample Au12x0/Au1550 PSC AC97 sound machine"
+	depends on SND_SOC_AU1XPSC
+	select SND_SOC_AU1XPSC_AC97
+	select SND_SOC_AC97_CODEC
+	help
+	  This is a sample AC97 sound machine for use in Au12x0/Au1550
+	  based systems which have audio on PSC1 (e.g. Db1200 demoboard).
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..6c6950b8003a834b9064ef7e6ffab77a11c211fc
--- /dev/null
+++ b/sound/soc/au1x/Makefile
@@ -0,0 +1,13 @@
+# Au1200/Au1550 PSC audio
+snd-soc-au1xpsc-dbdma-objs := dbdma2.o
+snd-soc-au1xpsc-i2s-objs := psc-i2s.o
+snd-soc-au1xpsc-ac97-objs := psc-ac97.o
+
+obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
+
+# Boards
+snd-soc-sample-ac97-objs := sample-ac97.o
+
+obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
new file mode 100644
index 0000000000000000000000000000000000000000..1466d932880026a99cd32f9e339c55fda0c3033f
--- /dev/null
+++ b/sound/soc/au1x/dbdma2.c
@@ -0,0 +1,421 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * DMA glue for Au1x-PSC audio.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ */
+
+
+#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/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/*#define PCM_DEBUG*/
+
+#define MSG(x...)	printk(KERN_INFO "au1xpsc_pcm: " x)
+#ifdef PCM_DEBUG
+#define DBG		MSG
+#else
+#define DBG(x...)	do {} while (0)
+#endif
+
+struct au1xpsc_audio_dmadata {
+	/* DDMA control data */
+	unsigned int ddma_id;		/* DDMA direction ID for this PSC */
+	u32 ddma_chan;			/* DDMA context */
+
+	/* PCM context (for irq handlers) */
+	struct snd_pcm_substream *substream;
+	unsigned long curr_period;	/* current segment DDMA is working on */
+	unsigned long q_period;		/* queue period(s) */
+	unsigned long dma_area;		/* address of queued DMA area */
+	unsigned long dma_area_s;	/* start address of DMA area */
+	unsigned long pos;		/* current byte position being played */
+	unsigned long periods;		/* number of SG segments in total */
+	unsigned long period_bytes;	/* size in bytes of one SG segment */
+
+	/* runtime data */
+	int msbits;
+};
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_dmadata *au1xpsc_audio_pcmdma[2];
+
+/*
+ * These settings are somewhat okay, at least on my machine audio plays
+ * almost skip-free. Especially the 64kB buffer seems to help a LOT.
+ */
+#define AU1XPSC_PERIOD_MIN_BYTES	1024
+#define AU1XPSC_BUFFER_MIN_BYTES	65536
+
+#define AU1XPSC_PCM_FMTS					\
+	(SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_U8 |	\
+	 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |	\
+	 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |	\
+	 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |	\
+	 SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE |	\
+	 0)
+
+/* PCM hardware DMA capabilities - platform specific */
+static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
+	.info		  = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+			    SNDRV_PCM_INFO_INTERLEAVED,
+	.formats	  = AU1XPSC_PCM_FMTS,
+	.period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
+	.period_bytes_max = 4096 * 1024 - 1,
+	.periods_min	  = 2,
+	.periods_max	  = 4096,	/* 2 to as-much-as-you-like */
+	.buffer_bytes_max = 4096 * 1024 - 1,
+	.fifo_size	  = 16,		/* fifo entries of AC97/I2S PSC */
+};
+
+static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
+{
+	au1xxx_dbdma_put_source_flags(cd->ddma_chan,
+				(void *)phys_to_virt(cd->dma_area),
+				cd->period_bytes, DDMA_FLAGS_IE);
+
+	/* update next-to-queue period */
+	++cd->q_period;
+	cd->dma_area += cd->period_bytes;
+	if (cd->q_period >= cd->periods) {
+		cd->q_period = 0;
+		cd->dma_area = cd->dma_area_s;
+	}
+}
+
+static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd)
+{
+	au1xxx_dbdma_put_dest_flags(cd->ddma_chan,
+				(void *)phys_to_virt(cd->dma_area),
+				cd->period_bytes, DDMA_FLAGS_IE);
+
+	/* update next-to-queue period */
+	++cd->q_period;
+	cd->dma_area += cd->period_bytes;
+	if (cd->q_period >= cd->periods) {
+		cd->q_period = 0;
+		cd->dma_area = cd->dma_area_s;
+	}
+}
+
+static void au1x_pcm_dmatx_cb(int irq, void *dev_id)
+{
+	struct au1xpsc_audio_dmadata *cd = dev_id;
+
+	cd->pos += cd->period_bytes;
+	if (++cd->curr_period >= cd->periods) {
+		cd->pos = 0;
+		cd->curr_period = 0;
+	}
+	snd_pcm_period_elapsed(cd->substream);
+	au1x_pcm_queue_tx(cd);
+}
+
+static void au1x_pcm_dmarx_cb(int irq, void *dev_id)
+{
+	struct au1xpsc_audio_dmadata *cd = dev_id;
+
+	cd->pos += cd->period_bytes;
+	if (++cd->curr_period >= cd->periods) {
+		cd->pos = 0;
+		cd->curr_period = 0;
+	}
+	snd_pcm_period_elapsed(cd->substream);
+	au1x_pcm_queue_rx(cd);
+}
+
+static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd)
+{
+	if (pcd->ddma_chan) {
+		au1xxx_dbdma_stop(pcd->ddma_chan);
+		au1xxx_dbdma_reset(pcd->ddma_chan);
+		au1xxx_dbdma_chan_free(pcd->ddma_chan);
+		pcd->ddma_chan = 0;
+		pcd->msbits = 0;
+	}
+}
+
+/* in case of missing DMA ring or changed TX-source / RX-dest bit widths,
+ * allocate (or reallocate) a 2-descriptor DMA ring with bit depth according
+ * to ALSA-supplied sample depth.  This is due to limitations in the dbdma api
+ * (cannot adjust source/dest widths of already allocated descriptor ring).
+ */
+static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
+				 int stype, int msbits)
+{
+	/* DMA only in 8/16/32 bit widths */
+	if (msbits == 24)
+		msbits = 32;
+
+	/* check current config: correct bits and descriptors allocated? */
+	if ((pcd->ddma_chan) && (msbits == pcd->msbits))
+		goto out;	/* all ok! */
+
+	au1x_pcm_dbdma_free(pcd);
+
+	if (stype == PCM_RX)
+		pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
+					DSCR_CMD0_ALWAYS,
+					au1x_pcm_dmarx_cb, (void *)pcd);
+	else
+		pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS,
+					pcd->ddma_id,
+					au1x_pcm_dmatx_cb, (void *)pcd);
+
+	if (!pcd->ddma_chan)
+		return -ENOMEM;;
+
+	au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits);
+	au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2);
+
+	pcd->msbits = msbits;
+
+	au1xxx_dbdma_stop(pcd->ddma_chan);
+	au1xxx_dbdma_reset(pcd->ddma_chan);
+
+out:
+	return 0;
+}
+
+static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct au1xpsc_audio_dmadata *pcd;
+	int stype, ret;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		goto out;
+
+	stype = SUBSTREAM_TYPE(substream);
+	pcd = au1xpsc_audio_pcmdma[stype];
+
+	DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
+	    "runtime->min_align %d\n",
+		(unsigned long)runtime->dma_area,
+		(unsigned long)runtime->dma_addr, runtime->dma_bytes,
+		runtime->min_align);
+
+	DBG("bits %d  frags %d  frag_bytes %d  is_rx %d\n", params->msbits,
+		params_periods(params), params_period_bytes(params), stype);
+
+	ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits);
+	if (ret) {
+		MSG("DDMA channel (re)alloc failed!\n");
+		goto out;
+	}
+
+	pcd->substream = substream;
+	pcd->period_bytes = params_period_bytes(params);
+	pcd->periods = params_periods(params);
+	pcd->dma_area_s = pcd->dma_area = (unsigned long)runtime->dma_addr;
+	pcd->q_period = 0;
+	pcd->curr_period = 0;
+	pcd->pos = 0;
+
+	ret = 0;
+out:
+	return ret;
+}
+
+static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct au1xpsc_audio_dmadata *pcd =
+			au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)];
+
+	au1xxx_dbdma_reset(pcd->ddma_chan);
+
+	if (SUBSTREAM_TYPE(substream) == PCM_RX) {
+		au1x_pcm_queue_rx(pcd);
+		au1x_pcm_queue_rx(pcd);
+	} else {
+		au1x_pcm_queue_tx(pcd);
+		au1x_pcm_queue_tx(pcd);
+	}
+
+	return 0;
+}
+
+static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	u32 c = au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->ddma_chan;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		au1xxx_dbdma_start(c);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		au1xxx_dbdma_stop(c);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t
+au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	return bytes_to_frames(substream->runtime,
+		au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->pos);
+}
+
+static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
+{
+	snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
+	return 0;
+}
+
+static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
+{
+	au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]);
+	return 0;
+}
+
+struct snd_pcm_ops au1xpsc_pcm_ops = {
+	.open		= au1xpsc_pcm_open,
+	.close		= au1xpsc_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= au1xpsc_pcm_hw_params,
+	.hw_free	= au1xpsc_pcm_hw_free,
+	.prepare	= au1xpsc_pcm_prepare,
+	.trigger	= au1xpsc_pcm_trigger,
+	.pointer	= au1xpsc_pcm_pointer,
+};
+
+static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int au1xpsc_pcm_new(struct snd_card *card,
+			   struct snd_soc_dai *dai,
+			   struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+		card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
+
+	return 0;
+}
+
+static int au1xpsc_pcm_probe(struct platform_device *pdev)
+{
+	struct resource *r;
+	int ret;
+
+	if (au1xpsc_audio_pcmdma[PCM_TX] || au1xpsc_audio_pcmdma[PCM_RX])
+		return -EBUSY;
+
+	/* TX DMA */
+	au1xpsc_audio_pcmdma[PCM_TX]
+		= kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+	if (!au1xpsc_audio_pcmdma[PCM_TX])
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out1;
+	}
+	(au1xpsc_audio_pcmdma[PCM_TX])->ddma_id = r->start;
+
+	/* RX DMA */
+	au1xpsc_audio_pcmdma[PCM_RX]
+		= kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+	if (!au1xpsc_audio_pcmdma[PCM_RX])
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!r) {
+		ret = -ENODEV;
+		goto out2;
+	}
+	(au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
+
+	return 0;
+
+out2:
+	kfree(au1xpsc_audio_pcmdma[PCM_RX]);
+	au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+out1:
+	kfree(au1xpsc_audio_pcmdma[PCM_TX]);
+	au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+	return ret;
+}
+
+static int au1xpsc_pcm_remove(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		if (au1xpsc_audio_pcmdma[i]) {
+			au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
+			kfree(au1xpsc_audio_pcmdma[i]);
+			au1xpsc_audio_pcmdma[i] = NULL;
+		}
+	}
+
+	return 0;
+}
+
+/* au1xpsc audio platform */
+struct snd_soc_platform au1xpsc_soc_platform = {
+	.name		= "au1xpsc-pcm-dbdma",
+	.probe		= au1xpsc_pcm_probe,
+	.remove		= au1xpsc_pcm_remove,
+	.pcm_ops 	= &au1xpsc_pcm_ops,
+	.pcm_new	= au1xpsc_pcm_new,
+	.pcm_free	= au1xpsc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
+
+static int __init au1xpsc_audio_dbdma_init(void)
+{
+	au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+	au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+	return 0;
+}
+
+static void __exit au1xpsc_audio_dbdma_exit(void)
+{
+}
+
+module_init(au1xpsc_audio_dbdma_init);
+module_exit(au1xpsc_audio_dbdma_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
new file mode 100644
index 0000000000000000000000000000000000000000..57facbad68254cc4b21ecb189bb3ad161afb495e
--- /dev/null
+++ b/sound/soc/au1x/psc-ac97.c
@@ -0,0 +1,387 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * Au1xxx-PSC AC97 glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+#define AC97_DIR	\
+	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AC97_RATES	\
+	SNDRV_PCM_RATE_8000_48000
+
+#define AC97_FMTS	\
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
+
+#define AC97PCR_START(stype)	\
+	((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
+#define AC97PCR_STOP(stype)	\
+	((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
+#define AC97PCR_CLRFIFO(stype)	\
+	((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
+
+/* AC97 controller reads codec register */
+static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
+					unsigned short reg)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	unsigned short data, tmo;
+
+	au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
+	au_sync();
+
+	tmo = 1000;
+	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+		udelay(2);
+
+	if (!tmo)
+		data = 0xffff;
+	else
+		data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+
+	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+	au_sync();
+
+	return data;
+}
+
+/* AC97 controller writes to codec register */
+static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+				unsigned short val)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	unsigned int tmo;
+
+	au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
+	au_sync();
+	tmo = 1000;
+	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+		au_sync();
+
+	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+	au_sync();
+}
+
+/* AC97 controller asserts a warm reset */
+static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+
+	au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
+	au_sync();
+	msleep(10);
+	au_writel(0, AC97_RST(pscdata));
+	au_sync();
+}
+
+static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	int i;
+
+	/* disable PSC during cold reset */
+	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
+	au_sync();
+
+	/* issue cold reset */
+	au_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
+	au_sync();
+	msleep(500);
+	au_writel(0, AC97_RST(pscdata));
+	au_sync();
+
+	/* enable PSC */
+	au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+	au_sync();
+
+	/* wait for PSC to indicate it's ready */
+	i = 100000;
+	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
+		au_sync();
+
+	if (i == 0) {
+		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
+		return;
+	}
+
+	/* enable the ac97 function */
+	au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+	au_sync();
+
+	/* wait for AC97 core to become ready */
+	i = 100000;
+	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
+		au_sync();
+	if (i == 0)
+		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read		= au1xpsc_ac97_read,
+	.write		= au1xpsc_ac97_write,
+	.reset		= au1xpsc_ac97_cold_reset,
+	.warm_reset	= au1xpsc_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	unsigned long r, stat;
+	int chans, stype = SUBSTREAM_TYPE(substream);
+
+	chans = params_channels(params);
+
+	r = au_readl(AC97_CFG(pscdata));
+	stat = au_readl(AC97_STAT(pscdata));
+
+	/* already active? */
+	if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
+		/* reject parameters not currently set up */
+		if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
+		    (pscdata->rate != params_rate(params)))
+			return -EINVAL;
+	} else {
+		/* disable AC97 device controller first */
+		au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+		au_sync();
+
+		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
+		r &= ~PSC_AC97CFG_LEN_MASK;
+		r |= PSC_AC97CFG_SET_LEN(params->msbits);
+
+		/* channels: enable slots for front L/R channel */
+		if (stype == PCM_TX) {
+			r &= ~PSC_AC97CFG_TXSLOT_MASK;
+			r |= PSC_AC97CFG_TXSLOT_ENA(3);
+			r |= PSC_AC97CFG_TXSLOT_ENA(4);
+		} else {
+			r &= ~PSC_AC97CFG_RXSLOT_MASK;
+			r |= PSC_AC97CFG_RXSLOT_ENA(3);
+			r |= PSC_AC97CFG_RXSLOT_ENA(4);
+		}
+
+		/* finally enable the AC97 controller again */
+		au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+		au_sync();
+
+		pscdata->cfg = r;
+		pscdata->rate = params_rate(params);
+	}
+
+	return 0;
+}
+
+static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
+				int cmd)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	int ret, stype = SUBSTREAM_TYPE(substream);
+
+	ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
+		au_sync();
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
+		au_sync();
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int au1xpsc_ac97_probe(struct platform_device *pdev,
+			      struct snd_soc_dai *dai)
+{
+	int ret;
+	struct resource *r;
+	unsigned long sel;
+
+	if (au1xpsc_ac97_workdata)
+		return -EBUSY;
+
+	au1xpsc_ac97_workdata =
+		kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+	if (!au1xpsc_ac97_workdata)
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out0;
+	}
+
+	ret = -EBUSY;
+	au1xpsc_ac97_workdata->ioarea =
+		request_mem_region(r->start, r->end - r->start + 1,
+					"au1xpsc_ac97");
+	if (!au1xpsc_ac97_workdata->ioarea)
+		goto out0;
+
+	au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
+	if (!au1xpsc_ac97_workdata->mmio)
+		goto out1;
+
+	/* configuration: max dma trigger threshold, enable ac97 */
+	 au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
+				      PSC_AC97CFG_TT_FIFO8 |
+				      PSC_AC97CFG_DE_ENABLE;
+
+	/* preserve PSC clock source set up by platform (dev.platform_data
+	 * is already occupied by soc layer)
+	 */
+	sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
+	au_sync();
+	/* next up: cold reset.  Dont check for PSC-ready now since
+	 * there may not be any codec clock yet.
+	 */
+
+	return 0;
+
+out1:
+	release_resource(au1xpsc_ac97_workdata->ioarea);
+	kfree(au1xpsc_ac97_workdata->ioarea);
+out0:
+	kfree(au1xpsc_ac97_workdata);
+	au1xpsc_ac97_workdata = NULL;
+	return ret;
+}
+
+static void au1xpsc_ac97_remove(struct platform_device *pdev,
+				struct snd_soc_dai *dai)
+{
+	/* disable PSC completely */
+	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+	au_sync();
+
+	iounmap(au1xpsc_ac97_workdata->mmio);
+	release_resource(au1xpsc_ac97_workdata->ioarea);
+	kfree(au1xpsc_ac97_workdata->ioarea);
+	kfree(au1xpsc_ac97_workdata);
+	au1xpsc_ac97_workdata = NULL;
+}
+
+static int au1xpsc_ac97_suspend(struct platform_device *pdev,
+				struct snd_soc_dai *dai)
+{
+	/* save interesting registers and disable PSC */
+	au1xpsc_ac97_workdata->pm[0] =
+			au_readl(PSC_SEL(au1xpsc_ac97_workdata));
+
+	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+	au_sync();
+
+	return 0;
+}
+
+static int au1xpsc_ac97_resume(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
+{
+	/* restore PSC clock config */
+	au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
+			PSC_SEL(au1xpsc_ac97_workdata));
+	au_sync();
+
+	/* after this point the ac97 core will cold-reset the codec.
+	 * During cold-reset the PSC is reinitialized and the last
+	 * configuration set up in hw_params() is restored.
+	 */
+	return 0;
+}
+
+struct snd_soc_dai au1xpsc_ac97_dai = {
+	.name			= "au1xpsc_ac97",
+	.type			= SND_SOC_DAI_AC97,
+	.probe			= au1xpsc_ac97_probe,
+	.remove			= au1xpsc_ac97_remove,
+	.suspend		= au1xpsc_ac97_suspend,
+	.resume			= au1xpsc_ac97_resume,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.ops = {
+		.trigger	= au1xpsc_ac97_trigger,
+		.hw_params	= au1xpsc_ac97_hw_params,
+	},
+};
+EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
+
+static int __init au1xpsc_ac97_init(void)
+{
+	au1xpsc_ac97_workdata = NULL;
+	return 0;
+}
+
+static void __exit au1xpsc_ac97_exit(void)
+{
+}
+
+module_init(au1xpsc_ac97_init);
+module_exit(au1xpsc_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
new file mode 100644
index 0000000000000000000000000000000000000000..ba4b5c199f21b9fa97aa62cf0f6166309e00a901
--- /dev/null
+++ b/sound/soc/au1x/psc-i2s.c
@@ -0,0 +1,414 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * Au1xxx-PSC I2S glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ * NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/* supported I2S DAI hardware formats */
+#define AU1XPSC_I2S_DAIFMT \
+	(SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J |	\
+	 SND_SOC_DAIFMT_NB_NF)
+
+/* supported I2S direction */
+#define AU1XPSC_I2S_DIR \
+	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AU1XPSC_I2S_RATES \
+	SNDRV_PCM_RATE_8000_192000
+
+#define AU1XPSC_I2S_FMTS \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+#define I2SSTAT_BUSY(stype)	\
+	((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
+#define I2SPCR_START(stype)	\
+	((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
+#define I2SPCR_STOP(stype)	\
+	((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
+#define I2SPCR_CLRFIFO(stype)	\
+	((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
+
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
+
+static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+			       unsigned int fmt)
+{
+	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+	unsigned long ct;
+	int ret;
+
+	ret = -EINVAL;
+
+	ct = pscdata->cfg;
+
+	ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ);	/* left-justified */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ct |= PSC_I2SCFG_XM;	/* enable I2S mode */
+		break;
+	case SND_SOC_DAIFMT_MSB:
+		break;
+	case SND_SOC_DAIFMT_LSB:
+		ct |= PSC_I2SCFG_MLJ;	/* LSB (right-) justified */
+		break;
+	default:
+		goto out;
+	}
+
+	ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI);		/* IB-IF */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ct |= PSC_I2SCFG_BI;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ct |= PSC_I2SCFG_WI;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		break;
+	default:
+		goto out;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:	/* CODEC master */
+		ct |= PSC_I2SCFG_MS;	/* PSC I2S slave mode */
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:	/* CODEC slave */
+		ct &= ~PSC_I2SCFG_MS;	/* PSC I2S Master mode */
+		break;
+	default:
+		goto out;
+	}
+
+	pscdata->cfg = ct;
+	ret = 0;
+out:
+	return ret;
+}
+
+static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+
+	int cfgbits;
+	unsigned long stat;
+
+	/* check if the PSC is already streaming data */
+	stat = au_readl(I2S_STAT(pscdata));
+	if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) {
+		/* reject parameters not currently set up in hardware */
+		cfgbits = au_readl(I2S_CFG(pscdata));
+		if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
+		    (params_rate(params) != pscdata->rate))
+			return -EINVAL;
+	} else {
+		/* set sample bitdepth */
+		pscdata->cfg &= ~(0x1f << 4);
+		pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits);
+		/* remember current rate for other stream */
+		pscdata->rate = params_rate(params);
+	}
+	return 0;
+}
+
+/* Configure PSC late:  on my devel systems the codec  is I2S master and
+ * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit.  ASoC
+ * uses aggressive PM and  switches the codec off  when it is not in use
+ * which also means the PSC unit doesn't get any clocks and is therefore
+ * dead. That's why this chunk here gets called from the trigger callback
+ * because I can be reasonably certain the codec is driving the clocks.
+ */
+static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
+{
+	unsigned long tmo;
+
+	/* bring PSC out of sleep, and configure I2S unit */
+	au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+	au_sync();
+
+	tmo = 1000000;
+	while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
+		tmo--;
+
+	if (!tmo)
+		goto psc_err;
+
+	au_writel(0, I2S_CFG(pscdata));
+	au_sync();
+	au_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
+	au_sync();
+
+	/* wait for I2S controller to become ready */
+	tmo = 1000000;
+	while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
+		tmo--;
+
+	if (tmo)
+		return 0;
+
+psc_err:
+	au_writel(0, I2S_CFG(pscdata));
+	au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+	au_sync();
+	return -ETIMEDOUT;
+}
+
+static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
+{
+	unsigned long tmo, stat;
+	int ret;
+
+	ret = 0;
+
+	/* if both TX and RX are idle, configure the PSC  */
+	stat = au_readl(I2S_STAT(pscdata));
+	if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
+		ret = au1xpsc_i2s_configure(pscdata);
+		if (ret)
+			goto out;
+	}
+
+	au_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
+	au_sync();
+	au_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
+	au_sync();
+
+	/* wait for start confirmation */
+	tmo = 1000000;
+	while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+		tmo--;
+
+	if (!tmo) {
+		au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+		au_sync();
+		ret = -ETIMEDOUT;
+	}
+out:
+	return ret;
+}
+
+static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
+{
+	unsigned long tmo, stat;
+
+	au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+	au_sync();
+
+	/* wait for stop confirmation */
+	tmo = 1000000;
+	while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+		tmo--;
+
+	/* if both TX and RX are idle, disable PSC */
+	stat = au_readl(I2S_STAT(pscdata));
+	if (!(stat & (PSC_I2SSTAT_RB | PSC_I2SSTAT_RB))) {
+		au_writel(0, I2S_CFG(pscdata));
+		au_sync();
+		au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+		au_sync();
+	}
+	return 0;
+}
+
+static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+	int ret, stype = SUBSTREAM_TYPE(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		ret = au1xpsc_i2s_start(pscdata, stype);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		ret = au1xpsc_i2s_stop(pscdata, stype);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int au1xpsc_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
+{
+	struct resource *r;
+	unsigned long sel;
+	int ret;
+
+	if (au1xpsc_i2s_workdata)
+		return -EBUSY;
+
+	au1xpsc_i2s_workdata =
+		kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+	if (!au1xpsc_i2s_workdata)
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out0;
+	}
+
+	ret = -EBUSY;
+	au1xpsc_i2s_workdata->ioarea =
+		request_mem_region(r->start, r->end - r->start + 1,
+					"au1xpsc_i2s");
+	if (!au1xpsc_i2s_workdata->ioarea)
+		goto out0;
+
+	au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
+	if (!au1xpsc_i2s_workdata->mmio)
+		goto out1;
+
+	/* preserve PSC clock source set up by platform (dev.platform_data
+	 * is already occupied by soc layer)
+	 */
+	sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
+	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+	au_sync();
+
+	/* preconfigure: set max rx/tx fifo depths */
+	au1xpsc_i2s_workdata->cfg |=
+			PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
+
+	/* don't wait for I2S core to become ready now; clocks may not
+	 * be running yet; depending on clock input for PSC a wait might
+	 * time out.
+	 */
+
+	return 0;
+
+out1:
+	release_resource(au1xpsc_i2s_workdata->ioarea);
+	kfree(au1xpsc_i2s_workdata->ioarea);
+out0:
+	kfree(au1xpsc_i2s_workdata);
+	au1xpsc_i2s_workdata = NULL;
+	return ret;
+}
+
+static void au1xpsc_i2s_remove(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
+{
+	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+
+	iounmap(au1xpsc_i2s_workdata->mmio);
+	release_resource(au1xpsc_i2s_workdata->ioarea);
+	kfree(au1xpsc_i2s_workdata->ioarea);
+	kfree(au1xpsc_i2s_workdata);
+	au1xpsc_i2s_workdata = NULL;
+}
+
+static int au1xpsc_i2s_suspend(struct platform_device *pdev,
+			       struct snd_soc_dai *cpu_dai)
+{
+	/* save interesting register and disable PSC */
+	au1xpsc_i2s_workdata->pm[0] =
+		au_readl(PSC_SEL(au1xpsc_i2s_workdata));
+
+	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+
+	return 0;
+}
+
+static int au1xpsc_i2s_resume(struct platform_device *pdev,
+			      struct snd_soc_dai *cpu_dai)
+{
+	/* select I2S mode and PSC clock */
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(au1xpsc_i2s_workdata->pm[0],
+			PSC_SEL(au1xpsc_i2s_workdata));
+	au_sync();
+
+	return 0;
+}
+
+struct snd_soc_dai au1xpsc_i2s_dai = {
+	.name			= "au1xpsc_i2s",
+	.type			= SND_SOC_DAI_I2S,
+	.probe			= au1xpsc_i2s_probe,
+	.remove			= au1xpsc_i2s_remove,
+	.suspend		= au1xpsc_i2s_suspend,
+	.resume			= au1xpsc_i2s_resume,
+	.playback = {
+		.rates		= AU1XPSC_I2S_RATES,
+		.formats	= AU1XPSC_I2S_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,	/* 2 without external help */
+	},
+	.capture = {
+		.rates		= AU1XPSC_I2S_RATES,
+		.formats	= AU1XPSC_I2S_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,	/* 2 without external help */
+	},
+	.ops = {
+		.trigger	= au1xpsc_i2s_trigger,
+		.hw_params	= au1xpsc_i2s_hw_params,
+	},
+	.dai_ops = {
+		.set_fmt	= au1xpsc_i2s_set_fmt,
+	},
+};
+EXPORT_SYMBOL(au1xpsc_i2s_dai);
+
+static int __init au1xpsc_i2s_init(void)
+{
+	au1xpsc_i2s_workdata = NULL;
+	return 0;
+}
+
+static void __exit au1xpsc_i2s_exit(void)
+{
+}
+
+module_init(au1xpsc_i2s_init);
+module_exit(au1xpsc_i2s_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
new file mode 100644
index 0000000000000000000000000000000000000000..8fdb1a04a07b6458c93d18faf0b9983aab947baf
--- /dev/null
+++ b/sound/soc/au1x/psc.h
@@ -0,0 +1,53 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * 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.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ */
+
+#ifndef _AU1X_PCM_H
+#define _AU1X_PCM_H
+
+extern struct snd_soc_dai au1xpsc_ac97_dai;
+extern struct snd_soc_dai au1xpsc_i2s_dai;
+extern struct snd_soc_platform au1xpsc_soc_platform;
+extern struct snd_ac97_bus_ops soc_ac97_ops;
+
+struct au1xpsc_audio_data {
+	void __iomem *mmio;
+
+	unsigned long cfg;
+	unsigned long rate;
+
+	unsigned long pm[2];
+	struct resource *ioarea;
+};
+
+#define PCM_TX	0
+#define PCM_RX	1
+
+#define SUBSTREAM_TYPE(substream) \
+	((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
+
+/* easy access macros */
+#define PSC_CTRL(x)	((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET)
+#define PSC_SEL(x)	((unsigned long)((x)->mmio) + PSC_SEL_OFFSET)
+#define I2S_STAT(x)	((unsigned long)((x)->mmio) + PSC_I2SSTAT_OFFSET)
+#define I2S_CFG(x)	((unsigned long)((x)->mmio) + PSC_I2SCFG_OFFSET)
+#define I2S_PCR(x)	((unsigned long)((x)->mmio) + PSC_I2SPCR_OFFSET)
+#define AC97_CFG(x)	((unsigned long)((x)->mmio) + PSC_AC97CFG_OFFSET)
+#define AC97_CDC(x)	((unsigned long)((x)->mmio) + PSC_AC97CDC_OFFSET)
+#define AC97_EVNT(x)	((unsigned long)((x)->mmio) + PSC_AC97EVNT_OFFSET)
+#define AC97_PCR(x)	((unsigned long)((x)->mmio) + PSC_AC97PCR_OFFSET)
+#define AC97_RST(x)	((unsigned long)((x)->mmio) + PSC_AC97RST_OFFSET)
+#define AC97_STAT(x)	((unsigned long)((x)->mmio) + PSC_AC97STAT_OFFSET)
+
+#endif
diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c
new file mode 100644
index 0000000000000000000000000000000000000000..f75ae7f62c3d4c157ec1b51729b972e173464474
--- /dev/null
+++ b/sound/soc/au1x/sample-ac97.c
@@ -0,0 +1,144 @@
+/*
+ * Sample Au12x0/Au1550 PSC AC97 sound machine.
+ *
+ * Copyright (c) 2007-2008 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms outlined in the file COPYING at the root of this
+ *  source archive.
+ *
+ * This is a very generic AC97 sound machine driver for boards which
+ * have (AC97) audio at PSC1 (e.g. DB1200 demoboards).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+
+#include "../codecs/ac97.h"
+#include "psc.h"
+
+static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_sync(codec);
+	return 0;
+}
+
+static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
+	.name		= "AC97",
+	.stream_name	= "AC97 HiFi",
+	.cpu_dai	= &au1xpsc_ac97_dai,	/* see psc-ac97.c */
+	.codec_dai	= &ac97_dai,		/* see codecs/ac97.c */
+	.init		= au1xpsc_sample_ac97_init,
+	.ops		= NULL,
+};
+
+static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
+	.name		= "Au1xxx PSC AC97 Audio",
+	.dai_link	= &au1xpsc_sample_ac97_dai,
+	.num_links	= 1,
+};
+
+static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
+	.machine	= &au1xpsc_sample_ac97_machine,
+	.platform	= &au1xpsc_soc_platform, /* see dbdma2.c */
+	.codec_dev	= &soc_codec_dev_ac97,
+};
+
+static struct resource au1xpsc_psc1_res[] = {
+	[0] = {
+		.start	= CPHYSADDR(PSC1_BASE_ADDR),
+		.end	= CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+#ifdef CONFIG_SOC_AU1200
+		.start	= AU1200_PSC1_INT,
+		.end	= AU1200_PSC1_INT,
+#elif defined(CONFIG_SOC_AU1550)
+		.start	= AU1550_PSC1_INT,
+		.end	= AU1550_PSC1_INT,
+#endif
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.start	= DSCR_CMD0_PSC1_TX,
+		.end	= DSCR_CMD0_PSC1_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[3] = {
+		.start	= DSCR_CMD0_PSC1_RX,
+		.end	= DSCR_CMD0_PSC1_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+static struct platform_device *au1xpsc_sample_ac97_dev;
+
+static int __init au1xpsc_sample_ac97_load(void)
+{
+	int ret;
+
+#ifdef CONFIG_SOC_AU1200
+	unsigned long io;
+
+	/* modify sys_pinfunc for AC97 on PSC1 */
+	io = au_readl(SYS_PINFUNC);
+	io |= SYS_PINFUNC_P1C;
+	io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B);
+	au_writel(io, SYS_PINFUNC);
+	au_sync();
+#endif
+
+	ret = -ENOMEM;
+
+	/* setup PSC clock source for AC97 part: external clock provided
+	 * by codec.  The psc-ac97.c driver depends on this setting!
+	 */
+	au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET);
+	au_sync();
+
+	au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1);
+	if (!au1xpsc_sample_ac97_dev)
+		goto out;
+
+	au1xpsc_sample_ac97_dev->resource =
+		kmemdup(au1xpsc_psc1_res, sizeof(struct resource) *
+			ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL);
+	au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res);
+	au1xpsc_sample_ac97_dev->id = 1;
+
+	platform_set_drvdata(au1xpsc_sample_ac97_dev,
+			     &au1xpsc_sample_ac97_devdata);
+	au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev;
+	ret = platform_device_add(au1xpsc_sample_ac97_dev);
+
+	if (ret) {
+		platform_device_put(au1xpsc_sample_ac97_dev);
+		au1xpsc_sample_ac97_dev = NULL;
+	}
+
+out:
+	return ret;
+}
+
+static void __exit au1xpsc_sample_ac97_exit(void)
+{
+	platform_device_unregister(au1xpsc_sample_ac97_dev);
+}
+
+module_init(au1xpsc_sample_ac97_load);
+module_exit(au1xpsc_sample_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3903ab7dfa4a78ea2e3eebcbf8526f832b5ebdc4..1db04a28a53d46c7732401a2375ea904db158af6 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,31 +1,37 @@
 config SND_SOC_AC97_CODEC
 	tristate
-	depends on SND_SOC
+	select SND_AC97_CODEC
+
+config SND_SOC_AK4535
+	tristate
+
+config SND_SOC_UDA1380
+        tristate
+
+config SND_SOC_WM8510
+	tristate
 
 config SND_SOC_WM8731
 	tristate
-	depends on SND_SOC
 
 config SND_SOC_WM8750
 	tristate
-	depends on SND_SOC
 
 config SND_SOC_WM8753
 	tristate
-	depends on SND_SOC
+
+config SND_SOC_WM8990
+	tristate
 
 config SND_SOC_WM9712
 	tristate
-	depends on SND_SOC
 
 config SND_SOC_WM9713
 	tristate
-	depends on SND_SOC
 
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
 	tristate
-	depends on SND_SOC
 
 # Cirrus Logic CS4270 Codec Hardware Mute Support
 # Select if you have external muting circuitry attached to your CS4270.
@@ -43,4 +49,4 @@ config SND_SOC_CS4270_VD33_ERRATA
 
 config SND_SOC_TLV320AIC3X
 	tristate
-	depends on SND_SOC && I2C
+	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 4e1314c9d3ecc44bd8228d383a3d9688bcf9dc45..d7b97abcf7291cea11e2ea29a3c687551f241b4f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,16 +1,24 @@
 snd-soc-ac97-objs := ac97.o
+snd-soc-ak4535-objs := ak4535.o
+snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.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_AK4535)	+= snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.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_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
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 2a1ffe39690887efcd9e5ea66628fca8e8d4d531..61fd96ca7bc782588e17655ebd8dd12887837e75 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -10,9 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    17th Oct 2005   Initial version.
- *
  * Generic AC97 support.
  */
 
@@ -24,6 +21,7 @@
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 #include <sound/soc.h>
+#include "ac97.h"
 
 #define AC97_VERSION "0.6"
 
@@ -43,7 +41,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
 		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
 		SNDRV_PCM_RATE_48000)
 
-struct snd_soc_codec_dai ac97_dai = {
+struct snd_soc_dai ac97_dai = {
 	.name = "AC97 HiFi",
 	.type = SND_SOC_DAI_AC97,
 	.playback = {
@@ -146,9 +144,34 @@ static int ac97_soc_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int ac97_soc_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_ac97_suspend(socdev->codec->ac97);
+
+	return 0;
+}
+
+static int ac97_soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_ac97_resume(socdev->codec->ac97);
+
+	return 0;
+}
+#else
+#define ac97_soc_suspend NULL
+#define ac97_soc_resume NULL
+#endif
+
 struct snd_soc_codec_device soc_codec_dev_ac97 = {
 	.probe = 	ac97_soc_probe,
 	.remove = 	ac97_soc_remove,
+	.suspend =	ac97_soc_suspend,
+	.resume =	ac97_soc_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
 
diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h
index 2bf6d69fd0692c204f3a3126edf3eb34ef39249a..281aa42e2bbb95c055ed0a48270a3a83a46d991e 100644
--- a/sound/soc/codecs/ac97.h
+++ b/sound/soc/codecs/ac97.h
@@ -14,6 +14,6 @@
 #define __LINUX_SND_SOC_AC97_H
 
 extern struct snd_soc_codec_device soc_codec_dev_ac97;
-extern struct snd_soc_codec_dai ac97_dai;
+extern struct snd_soc_dai ac97_dai;
 
 #endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
new file mode 100644
index 0000000000000000000000000000000000000000..b26003c4f3e850ba1559a9e158b1dca609fbda67
--- /dev/null
+++ b/sound/soc/codecs/ak4535.c
@@ -0,0 +1,696 @@
+/*
+ * ak4535.c  --  AK4535 ALSA Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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 "ak4535.h"
+
+#define AUDIO_NAME "ak4535"
+#define AK4535_VERSION "0.3"
+
+struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+/* codec private data */
+struct ak4535_priv {
+	unsigned int sysclk;
+};
+
+/*
+ * ak4535 register cache
+ */
+static const u16 ak4535_reg[AK4535_CACHEREGNUM] = {
+    0x0000, 0x0080, 0x0000, 0x0003,
+    0x0002, 0x0000, 0x0011, 0x0001,
+    0x0000, 0x0040, 0x0036, 0x0010,
+    0x0000, 0x0000, 0x0057, 0x0000,
+};
+
+/*
+ * read ak4535 register cache
+ */
+static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= AK4535_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u8 data;
+	data = reg;
+
+	if (codec->hw_write(codec->control_data, &data, 1) != 1)
+		return -EIO;
+
+	if (codec->hw_read(codec->control_data, &data, 1) != 1)
+		return -EIO;
+
+	return data;
+};
+
+/*
+ * write ak4535 register cache
+ */
+static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= AK4535_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the AK4535 register space
+ */
+static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D8 AK4535 register offset
+	 *   D7...D0 register data
+	 */
+	data[0] = reg & 0xff;
+	data[1] = value & 0xff;
+
+	ak4535_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+static int ak4535_sync(struct snd_soc_codec *codec)
+{
+	u16 *cache = codec->reg_cache;
+	int i, r = 0;
+
+	for (i = 0; i < AK4535_CACHEREGNUM; i++)
+		r |= ak4535_write(codec, i, cache[i]);
+
+	return r;
+};
+
+static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
+static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
+static const char *ak4535_mic_select[] = {"Internal", "External"};
+
+static const struct soc_enum ak4535_enum[] = {
+	SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
+	SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
+	SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
+	SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
+	SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
+};
+
+static const struct snd_kcontrol_new ak4535_snd_controls[] = {
+	SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
+	SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
+	SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
+	SOC_ENUM("Headphone Output", ak4535_enum[2]),
+	SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
+	SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
+	SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
+	SOC_ENUM("Mic Select", ak4535_enum[4]),
+	SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
+	SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
+	SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
+	SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
+	SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
+	SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
+	SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
+	SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
+	SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
+	SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
+	SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
+};
+
+/* add non dapm controls */
+static int ak4535_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+			snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
+	SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
+	SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
+	SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
+	SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4535_input_mux_control =
+	SOC_DAPM_ENUM("Input Select", ak4535_enum[4]);
+
+/* HP L switch */
+static const struct snd_kcontrol_new ak4535_hpl_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
+
+/* HP R switch */
+static const struct snd_kcontrol_new ak4535_hpr_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4535_mono2_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
+
+/* Line out switch */
+static const struct snd_kcontrol_new ak4535_line_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
+
+/* ak4535 dapm widgets */
+static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
+	SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+		&ak4535_stereo_mixer_controls[0],
+		ARRAY_SIZE(ak4535_stereo_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+		&ak4535_mono1_mixer_controls[0],
+		ARRAY_SIZE(ak4535_mono1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+		&ak4535_input_mixer_controls[0],
+		ARRAY_SIZE(ak4535_input_mixer_controls)),
+	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+		&ak4535_input_mux_control),
+	SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
+	SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_mono2_control),
+	/* speaker powersave bit */
+	SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_line_control),
+	SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_hpl_control),
+	SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_hpr_control),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("ROUT"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+	SND_SOC_DAPM_OUTPUT("SPP"),
+	SND_SOC_DAPM_OUTPUT("SPN"),
+	SND_SOC_DAPM_OUTPUT("MOUT1"),
+	SND_SOC_DAPM_OUTPUT("MOUT2"),
+	SND_SOC_DAPM_OUTPUT("MICOUT"),
+	SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0),
+	SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
+
+	SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
+	SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
+	SND_SOC_DAPM_INPUT("MICIN"),
+	SND_SOC_DAPM_INPUT("MICEXT"),
+	SND_SOC_DAPM_INPUT("AUX"),
+	SND_SOC_DAPM_INPUT("MIN"),
+	SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/*stereo mixer */
+	{"Stereo Mixer", "Playback Switch", "DAC"},
+	{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
+	{"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+	/* mono1 mixer */
+	{"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
+	{"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+	/* Mic */
+	{"Mic", NULL, "AIN"},
+	{"Input Mux", "Internal", "Mic Int Bias"},
+	{"Input Mux", "External", "Mic Ext Bias"},
+	{"Mic Int Bias", NULL, "MICIN"},
+	{"Mic Ext Bias", NULL, "MICEXT"},
+	{"MICOUT", NULL, "Input Mux"},
+
+	/* line out */
+	{"LOUT", NULL, "Line Out Enable"},
+	{"ROUT", NULL, "Line Out Enable"},
+	{"Line Out Enable", "Switch", "Line Out"},
+	{"Line Out", NULL, "Stereo Mixer"},
+
+	/* mono1 out */
+	{"MOUT1", NULL, "Mono Out"},
+	{"Mono Out", NULL, "Mono1 Mixer"},
+
+	/* left HP */
+	{"HPL", NULL, "Left HP Enable"},
+	{"Left HP Enable", "Switch", "HP L Amp"},
+	{"HP L Amp", NULL, "Stereo Mixer"},
+
+	/* right HP */
+	{"HPR", NULL, "Right HP Enable"},
+	{"Right HP Enable", "Switch", "HP R Amp"},
+	{"HP R Amp", NULL, "Stereo Mixer"},
+
+	/* speaker */
+	{"SPP", NULL, "Speaker Enable"},
+	{"SPN", NULL, "Speaker Enable"},
+	{"Speaker Enable", "Switch", "Spk Amp"},
+	{"Spk Amp", NULL, "MIN"},
+
+	/* mono 2 */
+	{"MOUT2", NULL, "Mono 2 Enable"},
+	{"Mono 2 Enable", "Switch", "Stereo Mixer"},
+
+	/* Aux In */
+	{"Aux In", NULL, "AUX"},
+
+	/* ADC */
+	{"ADC", NULL, "Input Mixer"},
+	{"Input Mixer", "Mic Capture Switch", "Mic"},
+	{"Input Mixer", "Aux Capture Switch", "Aux In"},
+};
+
+static int ak4535_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, ak4535_dapm_widgets,
+				  ARRAY_SIZE(ak4535_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static int ak4535_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 ak4535_priv *ak4535 = codec->private_data;
+
+	ak4535->sysclk = freq;
+	return 0;
+}
+
+static int ak4535_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 ak4535_priv *ak4535 = codec->private_data;
+	u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
+	int rate = params_rate(params), fs = 256;
+
+	if (rate)
+		fs = ak4535->sysclk / rate;
+
+	/* set fs */
+	switch (fs) {
+	case 1024:
+		mode2 |= (0x2 << 5);
+		break;
+	case 512:
+		mode2 |= (0x1 << 5);
+		break;
+	case 256:
+		break;
+	}
+
+	/* set rate */
+	ak4535_write(codec, AK4535_MODE2, mode2);
+	return 0;
+}
+
+static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 mode1 = 0;
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		mode1 = 0x0002;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		mode1 = 0x0001;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* use 32 fs for BCLK to save power */
+	mode1 |= 0x4;
+
+	ak4535_write(codec, AK4535_MODE1, mode1);
+	return 0;
+}
+
+static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
+	if (!mute)
+		ak4535_write(codec, AK4535_DAC, mute_reg);
+	else
+		ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
+	return 0;
+}
+
+static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 i;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		ak4535_mute(codec->dai, 0);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		ak4535_mute(codec->dai, 1);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		i = ak4535_read_reg_cache(codec, AK4535_PM1);
+		ak4535_write(codec, AK4535_PM1, i | 0x80);
+		i = ak4535_read_reg_cache(codec, AK4535_PM2);
+		ak4535_write(codec, AK4535_PM2, i & (~0x80));
+		break;
+	case SND_SOC_BIAS_OFF:
+		i = ak4535_read_reg_cache(codec, AK4535_PM1);
+		ak4535_write(codec, AK4535_PM1, i & (~0x80));
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai ak4535_dai = {
+	.name = "AK4535",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4535_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4535_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = ak4535_hw_params,
+	},
+	.dai_ops = {
+		.set_fmt = ak4535_set_dai_fmt,
+		.digital_mute = ak4535_mute,
+		.set_sysclk = ak4535_set_dai_sysclk,
+	},
+};
+EXPORT_SYMBOL_GPL(ak4535_dai);
+
+static int ak4535_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;
+
+	ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int ak4535_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	ak4535_sync(codec);
+	ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	ak4535_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the AK4535 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ak4535_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "AK4535";
+	codec->owner = THIS_MODULE;
+	codec->read = ak4535_read_reg_cache;
+	codec->write = ak4535_write;
+	codec->set_bias_level = ak4535_set_bias_level;
+	codec->dai = &ak4535_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(ak4535_reg);
+	codec->reg_cache = kmemdup(ak4535_reg, sizeof(ak4535_reg), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "ak4535: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* power on device */
+	ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	ak4535_add_controls(codec);
+	ak4535_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "ak4535: 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 *ak4535_socdev;
+
+#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)
+{
+	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) {
+		kfree(codec);
+		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) {
+		printk(KERN_ERR "failed to initialise AK4535\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int ak4535_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 ak4535_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, ak4535_codec_probe);
+}
+
+/* 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,
+};
+
+static struct i2c_client client_template = {
+	.name =   "AK4535",
+	.driver = &ak4535_i2c_driver,
+};
+#endif
+
+static int ak4535_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct ak4535_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct ak4535_priv *ak4535;
+	int ret = 0;
+
+	printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL);
+	if (ak4535 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = ak4535;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ak4535_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)i2c_master_recv;
+		ret = i2c_add_driver(&ak4535_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 ak4535_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)
+		ak4535_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(&ak4535_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4535 = {
+	.probe = 	ak4535_probe,
+	.remove = 	ak4535_remove,
+	.suspend = 	ak4535_suspend,
+	.resume =	ak4535_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
+
+MODULE_DESCRIPTION("Soc AK4535 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
new file mode 100644
index 0000000000000000000000000000000000000000..e9fe30e2c05613f3f500aeea1d98115f9b29e508
--- /dev/null
+++ b/sound/soc/codecs/ak4535.h
@@ -0,0 +1,46 @@
+/*
+ * ak4535.h  --  AK4535 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.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 _AK4535_H
+#define _AK4535_H
+
+/* AK4535 register space */
+
+#define AK4535_PM1		0x0
+#define AK4535_PM2		0x1
+#define AK4535_SIG1		0x2
+#define AK4535_SIG2		0x3
+#define AK4535_MODE1		0x4
+#define AK4535_MODE2		0x5
+#define AK4535_DAC		0x6
+#define AK4535_MIC		0x7
+#define AK4535_TIMER		0x8
+#define AK4535_ALC1		0x9
+#define AK4535_ALC2		0xa
+#define AK4535_PGA		0xb
+#define AK4535_LATT		0xc
+#define AK4535_RATT		0xd
+#define AK4535_VOL		0xe
+#define AK4535_STATUS		0xf
+
+#define AK4535_CACHEREGNUM 	0x10
+
+struct ak4535_setup_data {
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ak4535_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e73fcfd9f5cd41638acbc8d3ad5e30f532fe829f..9deb8c74fdfd3f1c07f5b1eaf518d4b8f145b5d6 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -201,7 +201,7 @@ static struct {
  * driver what the input settings can be.  This would need to be implemented
  * for stand-alone mode to work.
  */
-static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_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;
@@ -251,7 +251,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
  * data for playback only, but ASoC currently does not support different
  * formats for playback vs. record.
  */
-static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
 			      unsigned int format)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -471,7 +471,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
  * board does not have the MUTEA or MUTEB pins connected to such circuitry,
  * then this function will do nothing.
  */
-static int cs4270_mute(struct snd_soc_codec_dai *dai, int mute)
+static int cs4270_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	int reg6;
@@ -667,7 +667,7 @@ error:
 
 #endif /* USE_I2C*/
 
-struct snd_soc_codec_dai cs4270_dai = {
+struct snd_soc_dai cs4270_dai = {
 	.name = "CS4270",
 	.playback = {
 		.stream_name = "Playback",
diff --git a/sound/soc/codecs/cs4270.h b/sound/soc/codecs/cs4270.h
index 0ced49b7804db22f46719e4ee6957e7e96e908d1..adc6cd9667d4275b3a1035d15de5b5a9c04d0fbf 100644
--- a/sound/soc/codecs/cs4270.h
+++ b/sound/soc/codecs/cs4270.h
@@ -16,7 +16,7 @@
  * The ASoC codec DAI structure for the CS4270.  Assign this structure to
  * the .codec_dai field of your machine driver's snd_soc_dai_link structure.
  */
-extern struct snd_soc_codec_dai cs4270_dai;
+extern struct snd_soc_dai cs4270_dai;
 
 /*
  * The ASoC codec device structure for the CS4270.  Assign this structure
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 09b1661b8a3a3930720947d91cf9a582b642cb0f..b1dce5f459db6d40663112ecb2e3f8eb13bb30a9 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -29,7 +29,7 @@
  *  ---------------------------------------
  *
  *  Hence the machine layer should disable unsupported inputs/outputs by
- *  snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc.
+ *  snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc.
  */
 
 #include <linux/module.h>
@@ -49,7 +49,7 @@
 #include "tlv320aic3x.h"
 
 #define AUDIO_NAME "aic3x"
-#define AIC3X_VERSION "0.1"
+#define AIC3X_VERSION "0.2"
 
 /* codec private data */
 struct aic3x_priv {
@@ -138,6 +138,20 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
 		return -EIO;
 }
 
+/*
+ * read from the aic3x register space
+ */
+static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
+		      u8 *value)
+{
+	*value = reg & 0xff;
+	if (codec->hw_read(codec->control_data, value, 1) != 1)
+		return -EIO;
+
+	aic3x_write_reg_cache(codec, reg, *value);
+	return 0;
+}
+
 #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
@@ -192,7 +206,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
 		}
 
 		if (found)
-			snd_soc_dapm_sync_endpoints(widget->codec);
+			snd_soc_dapm_sync(widget->codec);
 	}
 
 	ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
@@ -209,6 +223,8 @@ static const char *aic3x_right_hpcom_mux[] =
     { "differential of HPROUT", "constant VCM", "single-ended",
       "differential of HPLCOM", "external feedback" };
 static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
+static const char *aic3x_adc_hpf[] =
+    { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
 
 #define LDAC_ENUM	0
 #define RDAC_ENUM	1
@@ -218,6 +234,7 @@ static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
 #define LINE1R_ENUM	5
 #define LINE2L_ENUM	6
 #define LINE2R_ENUM	7
+#define ADC_HPF_ENUM	8
 
 static const struct soc_enum aic3x_enum[] = {
 	SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
@@ -228,6 +245,7 @@ static const struct soc_enum aic3x_enum[] = {
 	SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
 	SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
 	SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+	SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
 };
 
 static const struct snd_kcontrol_new aic3x_snd_controls[] = {
@@ -278,6 +296,8 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 	/* Input */
 	SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
 	SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
+
+	SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
 };
 
 /* add non dapm controls */
@@ -441,11 +461,34 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
 	SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
 			 &aic3x_right_line2_mux_controls),
 
+	/*
+	 * Not a real mic bias widget but similar function. This is for dynamic
+	 * control of GPIO1 digital mic modulator clock output function when
+	 * using digital mic.
+	 */
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "GPIO1 dmic modclk",
+			 AIC3X_GPIO1_REG, 4, 0xf,
+			 AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK,
+			 AIC3X_GPIO1_FUNC_DISABLED),
+
+	/*
+	 * Also similar function like mic bias. Selects digital mic with
+	 * configurable oversampling rate instead of ADC converter.
+	 */
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 128",
+			 AIC3X_ASD_INTF_CTRLA, 0, 3, 1, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 64",
+			 AIC3X_ASD_INTF_CTRLA, 0, 3, 2, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32",
+			 AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
+
 	/* Mic Bias */
-	SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0),
-	SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0),
-	SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0),
-	SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V",
+			 MICBIAS_CTRL, 6, 3, 1, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V",
+			 MICBIAS_CTRL, 6, 3, 2, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD",
+			 MICBIAS_CTRL, 6, 3, 3, 0),
 
 	/* Left PGA to Left Output bypass */
 	SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
@@ -483,7 +526,7 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
 	SND_SOC_DAPM_INPUT("LINE2R"),
 };
 
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
 	/* Left Output */
 	{"Left DAC Mux", "DAC_L1", "Left DAC"},
 	{"Left DAC Mux", "DAC_L2", "Left DAC"},
@@ -554,6 +597,7 @@ static const char *intercon[][3] = {
 	{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
 
 	{"Left ADC", NULL, "Left PGA Mixer"},
+	{"Left ADC", NULL, "GPIO1 dmic modclk"},
 
 	/* Right Input */
 	{"Right Line1R Mux", "single-ended", "LINE1R"},
@@ -567,6 +611,7 @@ static const char *intercon[][3] = {
 	{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
 
 	{"Right ADC", NULL, "Right PGA Mixer"},
+	{"Right ADC", NULL, "GPIO1 dmic modclk"},
 
 	/* Left PGA Bypass */
 	{"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
@@ -628,101 +673,27 @@ static const char *intercon[][3] = {
 	{"Mono Out", NULL, "Right Line2 Bypass Mixer"},
 	{"Right HP Out", NULL, "Right Line2 Bypass Mixer"},
 
-	/* terminator */
-	{NULL, NULL, NULL},
+	/*
+	 * Logical path between digital mic enable and GPIO1 modulator clock
+	 * output function
+	 */
+	{"GPIO1 dmic modclk", NULL, "DMic Rate 128"},
+	{"GPIO1 dmic modclk", NULL, "DMic Rate 64"},
+	{"GPIO1 dmic modclk", NULL, "DMic Rate 32"},
 };
 
 static int aic3x_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+				  ARRAY_SIZE(aic3x_dapm_widgets));
 
 	/* set up audio path interconnects */
-	for (i = 0; intercon[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, intercon[i][0],
-					   intercon[i][1], intercon[i][2]);
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
 }
 
-struct aic3x_rate_divs {
-	u32 mclk;
-	u32 rate;
-	u32 fsref_reg;
-	u8 sr_reg:4;
-	u8 pllj_reg;
-	u16 plld_reg;
-};
-
-/* AIC3X codec mclk clock divider coefficients */
-static const struct aic3x_rate_divs aic3x_divs[] = {
-	/* 8k */
-	{12000000, 8000, 48000, 0xa, 16, 3840},
-	{19200000, 8000, 48000, 0xa, 10, 2400},
-	{22579200, 8000, 48000, 0xa, 8, 7075},
-	{33868800, 8000, 48000, 0xa, 5, 8049},
-	/* 11.025k */
-	{12000000, 11025, 44100, 0x6, 15, 528},
-	{19200000, 11025, 44100, 0x6, 9, 4080},
-	{22579200, 11025, 44100, 0x6, 8, 0},
-	{33868800, 11025, 44100, 0x6, 5, 3333},
-	/* 16k */
-	{12000000, 16000, 48000, 0x4, 16, 3840},
-	{19200000, 16000, 48000, 0x4, 10, 2400},
-	{22579200, 16000, 48000, 0x4, 8, 7075},
-	{33868800, 16000, 48000, 0x4, 5, 8049},
-	/* 22.05k */
-	{12000000, 22050, 44100, 0x2, 15, 528},
-	{19200000, 22050, 44100, 0x2, 9, 4080},
-	{22579200, 22050, 44100, 0x2, 8, 0},
-	{33868800, 22050, 44100, 0x2, 5, 3333},
-	/* 32k */
-	{12000000, 32000, 48000, 0x1, 16, 3840},
-	{19200000, 32000, 48000, 0x1, 10, 2400},
-	{22579200, 32000, 48000, 0x1, 8, 7075},
-	{33868800, 32000, 48000, 0x1, 5, 8049},
-	/* 44.1k */
-	{12000000, 44100, 44100, 0x0, 15, 528},
-	{19200000, 44100, 44100, 0x0, 9, 4080},
-	{22579200, 44100, 44100, 0x0, 8, 0},
-	{33868800, 44100, 44100, 0x0, 5, 3333},
-	/* 48k */
-	{12000000, 48000, 48000, 0x0, 16, 3840},
-	{19200000, 48000, 48000, 0x0, 10, 2400},
-	{22579200, 48000, 48000, 0x0, 8, 7075},
-	{33868800, 48000, 48000, 0x0, 5, 8049},
-	/* 64k */
-	{12000000, 64000, 96000, 0x1, 16, 3840},
-	{19200000, 64000, 96000, 0x1, 10, 2400},
-	{22579200, 64000, 96000, 0x1, 8, 7075},
-	{33868800, 64000, 96000, 0x1, 5, 8049},
-	/* 88.2k */
-	{12000000, 88200, 88200, 0x0, 15, 528},
-	{19200000, 88200, 88200, 0x0, 9, 4080},
-	{22579200, 88200, 88200, 0x0, 8, 0},
-	{33868800, 88200, 88200, 0x0, 5, 3333},
-	/* 96k */
-	{12000000, 96000, 96000, 0x0, 16, 3840},
-	{19200000, 96000, 96000, 0x0, 10, 2400},
-	{22579200, 96000, 96000, 0x0, 8, 7075},
-	{33868800, 96000, 96000, 0x0, 5, 8049},
-};
-
-static inline int aic3x_get_divs(int mclk, int rate)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) {
-		if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk)
-			return i;
-	}
-
-	return 0;
-}
-
 static int aic3x_hw_params(struct snd_pcm_substream *substream,
 			   struct snd_pcm_hw_params *params)
 {
@@ -730,49 +701,107 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
-	int i;
-	u8 data, pll_p, pll_r, pll_j;
-	u16 pll_d;
-
-	i = aic3x_get_divs(aic3x->sysclk, params_rate(params));
+	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
+	u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+	u16 pll_d = 1;
 
-	/* Route Left DAC to left channel input and
-	 * right DAC to right channel input */
-	data = (LDAC2LCH | RDAC2RCH);
-	switch (aic3x_divs[i].fsref_reg) {
-	case 44100:
-		data |= FSREF_44100;
+	/* select data word length */
+	data =
+	    aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
 		break;
-	case 48000:
-		data |= FSREF_48000;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		data |= (0x01 << 4);
 		break;
-	case 88200:
-		data |= FSREF_44100 | DUAL_RATE_MODE;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		data |= (0x02 << 4);
 		break;
-	case 96000:
-		data |= FSREF_48000 | DUAL_RATE_MODE;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		data |= (0x03 << 4);
 		break;
 	}
+	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
+
+	/* Fsref can be 44100 or 48000 */
+	fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+
+	/* Try to find a value for Q which allows us to bypass the PLL and
+	 * generate CODEC_CLK directly. */
+	for (pll_q = 2; pll_q < 18; pll_q++)
+		if (aic3x->sysclk / (128 * pll_q) == fsref) {
+			bypass_pll = 1;
+			break;
+		}
+
+	if (bypass_pll) {
+		pll_q &= 0xf;
+		aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
+	} else
+		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+
+	/* Route Left DAC to left channel input and
+	 * right DAC to right channel input */
+	data = (LDAC2LCH | RDAC2RCH);
+	data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
+	if (params_rate(params) >= 64000)
+		data |= DUAL_RATE_MODE;
 	aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
 
 	/* codec sample rate select */
-	data = aic3x_divs[i].sr_reg;
+	data = (fsref * 20) / params_rate(params);
+	if (params_rate(params) < 64000)
+		data /= 2;
+	data /= 5;
+	data -= 2;
 	data |= (data << 4);
 	aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
 
-	/* Use PLL for generation Fsref by equation:
-	 * Fsref = (MCLK * K * R)/(2048 * P);
-	 * Fix P = 2 and R = 1 and calculate K, if
-	 * K = J.D, i.e. J - an interger portion of K and D is the fractional
-	 * one with 4 digits of precision;
-	 * Example:
-	 * For MCLK = 22.5792 MHz and Fsref = 48kHz:
-	 * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074
+	if (bypass_pll)
+		return 0;
+
+	/* Use PLL
+	 * find an apropriate setup for j, d, r and p by iterating over
+	 * p and r - j and d are calculated for each fraction.
+	 * Up to 128 values are probed, the closest one wins the game.
+	 * The sysclk is divided by 1000 to prevent integer overflows.
 	 */
-	pll_p = 2;
-	pll_r = 1;
-	pll_j = aic3x_divs[i].pllj_reg;
-	pll_d = aic3x_divs[i].plld_reg;
+	codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
+
+	for (r = 1; r <= 16; r++)
+		for (p = 1; p <= 8; p++) {
+			int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
+			u8 j = tmp / 10000;
+			u16 d = tmp % 10000;
+
+			if (j > 63)
+				continue;
+
+			if (d != 0 && aic3x->sysclk < 10000000)
+				continue;
+
+			/* This is actually 1000 * ((j + (d/10000)) * r) / p
+			 * The term had to be converted to get rid of the
+			 * division by 10000 */
+			clk = ((10000 * j * r) + (d * r)) / (10 * p);
+
+			/* check whether this values get closer than the best
+			 * ones we had before */
+			if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+				pll_j = j; pll_d = d; pll_r = r; pll_p = p;
+				last_clk = clk;
+			}
+
+			/* Early exit for exact matches */
+			if (clk == codec_clk)
+				break;
+		}
+
+	if (last_clk == 0) {
+		printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
+		return -EINVAL;
+	}
 
 	data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
 	aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
@@ -782,28 +811,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
 	aic3x_write(codec, AIC3X_PLL_PROGD_REG,
 		    (pll_d & 0x3F) << PLLD_LSB_SHIFT);
 
-	/* select data word length */
-	data =
-	    aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
-	switch (params_format(params)) {
-	case SNDRV_PCM_FORMAT_S16_LE:
-		break;
-	case SNDRV_PCM_FORMAT_S20_3LE:
-		data |= (0x01 << 4);
-		break;
-	case SNDRV_PCM_FORMAT_S24_LE:
-		data |= (0x02 << 4);
-		break;
-	case SNDRV_PCM_FORMAT_S32_LE:
-		data |= (0x03 << 4);
-		break;
-	}
-	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
-
 	return 0;
 }
 
-static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
+static int aic3x_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
@@ -820,31 +831,25 @@ static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
 	return 0;
 }
 
-static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_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 aic3x_priv *aic3x = codec->private_data;
 
-	switch (freq) {
-	case 12000000:
-	case 19200000:
-	case 22579200:
-	case 33868800:
-		aic3x->sysclk = freq;
-		return 0;
-	}
-
-	return -EINVAL;
+	aic3x->sysclk = freq;
+	return 0;
 }
 
-static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 			     unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
-	u8 iface_areg = 0;
-	u8 iface_breg = 0;
+	u8 iface_areg, iface_breg;
+
+	iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+	iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -883,13 +888,14 @@ static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 	return 0;
 }
 
-static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
+static int aic3x_set_bias_level(struct snd_soc_codec *codec,
+				enum snd_soc_bias_level level)
 {
 	struct aic3x_priv *aic3x = codec->private_data;
 	u8 reg;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0:
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* all power is driven by DAPM system */
 		if (aic3x->master) {
 			/* enable pll */
@@ -898,10 +904,9 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
 				    reg | PLL_ENABLE);
 		}
 		break;
-	case SNDRV_CTL_POWER_D1:
-	case SNDRV_CTL_POWER_D2:
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot:
+	case SND_SOC_BIAS_STANDBY:
 		/*
 		 * all power is driven by DAPM system,
 		 * so output power is safe if bypass was set
@@ -913,7 +918,7 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
 				    reg & ~PLL_ENABLE);
 		}
 		break;
-	case SNDRV_CTL_POWER_D3cold:
+	case SND_SOC_BIAS_OFF:
 		/* force all power off */
 		reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
 		aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
@@ -949,16 +954,43 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
 		}
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 
 	return 0;
 }
 
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state)
+{
+	u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+	u8 bit = gpio ? 3: 0;
+	u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
+	aic3x_write(codec, reg, val | (!!state << bit));
+}
+EXPORT_SYMBOL_GPL(aic3x_set_gpio);
+
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
+{
+	u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+	u8 val, bit = gpio ? 2: 1;
+
+	aic3x_read(codec, reg, &val);
+	return (val >> bit) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_get_gpio);
+
+int aic3x_headset_detected(struct snd_soc_codec *codec)
+{
+	u8 val;
+	aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
+	return (val >> 2) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_headset_detected);
+
 #define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000
 #define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
 
-struct snd_soc_codec_dai aic3x_dai = {
+struct snd_soc_dai aic3x_dai = {
 	.name = "aic3x",
 	.playback = {
 		.stream_name = "Playback",
@@ -988,7 +1020,7 @@ static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->codec;
 
-	aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
@@ -1008,7 +1040,7 @@ static int aic3x_resume(struct platform_device *pdev)
 		codec->hw_write(codec->control_data, data, 2);
 	}
 
-	aic3x_dapm_event(codec, codec->suspend_dapm_state);
+	aic3x_set_bias_level(codec, codec->suspend_bias_level);
 
 	return 0;
 }
@@ -1020,16 +1052,17 @@ static int aic3x_resume(struct platform_device *pdev)
 static int aic3x_init(struct snd_soc_device *socdev)
 {
 	struct snd_soc_codec *codec = socdev->codec;
+	struct aic3x_setup_data *setup = socdev->codec_data;
 	int reg, ret = 0;
 
 	codec->name = "aic3x";
 	codec->owner = THIS_MODULE;
 	codec->read = aic3x_read_reg_cache;
 	codec->write = aic3x_write;
-	codec->dapm_event = aic3x_dapm_event;
+	codec->set_bias_level = aic3x_set_bias_level;
 	codec->dai = &aic3x_dai;
 	codec->num_dai = 1;
-	codec->reg_cache_size = sizeof(aic3x_reg);
+	codec->reg_cache_size = ARRAY_SIZE(aic3x_reg);
 	codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
@@ -1108,7 +1141,11 @@ static int aic3x_init(struct snd_soc_device *socdev)
 	aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
 
 	/* off, with power on */
-	aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* setup GPIO functions */
+	aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
+	aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
 
 	aic3x_add_controls(codec);
 	aic3x_add_widgets(codec);
@@ -1217,6 +1254,12 @@ static struct i2c_client client_template = {
 	.name = "AIC3X",
 	.driver = &aic3x_i2c_driver,
 };
+
+static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
+{
+	value[0] = i2c_smbus_read_byte_data(client, value[0]);
+	return (len == 1);
+}
 #endif
 
 static int aic3x_probe(struct platform_device *pdev)
@@ -1251,6 +1294,7 @@ static int aic3x_probe(struct platform_device *pdev)
 	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");
@@ -1268,7 +1312,7 @@ static int aic3x_remove(struct platform_device *pdev)
 
 	/* power down chip */
 	if (codec->control_data)
-		aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3);
+		aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index d0cdeeb629de5f0a12435f78b5fb2fbe4d4a9a7b..d76c079b86e7b52f148b3a35a49ff0ea2f9a7b8d 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -37,6 +37,8 @@
 #define AIC3X_ASD_INTF_CTRLB		9
 /* Audio overflow status and PLL R value programming register */
 #define AIC3X_OVRF_STATUS_AND_PLLR_REG	11
+/* Audio codec digital filter control register */
+#define AIC3X_CODEC_DFILT_CTRL		12
 
 /* ADC PGA Gain control registers */
 #define LADC_VOL			15
@@ -108,6 +110,13 @@
 #define DACR1_2_RLOPM_VOL		92
 #define LLOPM_CTRL			86
 #define RLOPM_CTRL			93
+/* GPIO/IRQ registers */
+#define AIC3X_STICKY_IRQ_FLAGS_REG	96
+#define AIC3X_RT_IRQ_FLAGS_REG		97
+#define AIC3X_GPIO1_REG			98
+#define AIC3X_GPIO2_REG			99
+#define AIC3X_GPIOA_REG			100
+#define AIC3X_GPIOB_REG			101
 /* Clock generation control register */
 #define AIC3X_CLKGEN_CTRL_REG		102
 
@@ -128,12 +137,15 @@
 
 /* PLL registers bitfields */
 #define PLLP_SHIFT		0
+#define PLLQ_SHIFT		3
 #define PLLR_SHIFT		0
 #define PLLJ_SHIFT		2
 #define PLLD_MSB_SHIFT		0
 #define PLLD_LSB_SHIFT		2
 
 /* Clock generation register bits */
+#define CODEC_CLKIN_PLLDIV	0
+#define CODEC_CLKIN_CLKDIV	1
 #define PLL_CLKIN_SHIFT		4
 #define MCLK_SOURCE		0x0
 #define PLL_CLKDIV_SHIFT	0
@@ -171,11 +183,52 @@
 /* Default input volume */
 #define DEFAULT_GAIN    0x20
 
+/* GPIO API */
+enum {
+	AIC3X_GPIO1_FUNC_DISABLED		= 0,
+	AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC	= 1,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX		= 2,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2		= 3,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4		= 4,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8		= 5,
+	AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ	= 6,
+	AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ		= 7,
+	AIC3X_GPIO1_FUNC_INPUT			= 8,
+	AIC3X_GPIO1_FUNC_OUTPUT			= 9,
+	AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK	= 10,
+	AIC3X_GPIO1_FUNC_AUDIO_WORDCLK		= 11,
+	AIC3X_GPIO1_FUNC_BUTTON_IRQ		= 12,
+	AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ	= 13,
+	AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ	= 14,
+	AIC3X_GPIO1_FUNC_ALL_IRQ		= 16
+};
+
+enum {
+	AIC3X_GPIO2_FUNC_DISABLED		= 0,
+	AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ	= 2,
+	AIC3X_GPIO2_FUNC_INPUT			= 3,
+	AIC3X_GPIO2_FUNC_OUTPUT			= 4,
+	AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT	= 5,
+	AIC3X_GPIO2_FUNC_AUDIO_BITCLK		= 8,
+	AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9,
+	AIC3X_GPIO2_FUNC_ALL_IRQ		= 10,
+	AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11,
+	AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12,
+	AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ	= 13,
+	AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ		= 14,
+	AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ	= 15
+};
+
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
+int aic3x_headset_detected(struct snd_soc_codec *codec);
+
 struct aic3x_setup_data {
 	unsigned short i2c_address;
+	unsigned int gpio_func[2];
 };
 
-extern struct snd_soc_codec_dai aic3x_dai;
+extern struct snd_soc_dai aic3x_dai;
 extern struct snd_soc_codec_device soc_codec_dev_aic3x;
 
 #endif /* _AIC3X_H */
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
new file mode 100644
index 0000000000000000000000000000000000000000..a52d6d9e007adf718fd25ac951c9eb55e1251cec
--- /dev/null
+++ b/sound/soc/codecs/uda1380.c
@@ -0,0 +1,852 @@
+/*
+ * uda1380.c - Philips UDA1380 ALSA 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.
+ *
+ * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
+ * Improved support for DAPM and audio routing/mixing capabilities,
+ * added TLV support.
+ *
+ * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
+ * codec model.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright 2005 Openedhand Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "uda1380.h"
+
+#define UDA1380_VERSION "0.6"
+#define AUDIO_NAME "uda1380"
+
+/*
+ * uda1380 register cache
+ */
+static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
+	0x0502, 0x0000, 0x0000, 0x3f3f,
+	0x0202, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0xff00, 0x0000, 0x4800,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x8000, 0x0002, 0x0000,
+};
+
+/*
+ * read uda1380 register cache
+ */
+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg == UDA1380_RESET)
+		return 0;
+	if (reg >= UDA1380_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write uda1380 register cache
+ */
+static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= UDA1380_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the UDA1380 register space
+ */
+static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[3];
+
+	/* data is
+	 *   data[0] is register offset
+	 *   data[1] is MS byte
+	 *   data[2] is LS byte
+	 */
+	data[0] = reg;
+	data[1] = (value & 0xff00) >> 8;
+	data[2] = value & 0x00ff;
+
+	uda1380_write_reg_cache(codec, reg, value);
+
+	/* the interpolator & decimator regs must only be written when the
+	 * codec DAI is active.
+	 */
+	if (!codec->active && (reg >= UDA1380_MVOL))
+		return 0;
+	pr_debug("uda1380: hw write %x val %x\n", reg, value);
+	if (codec->hw_write(codec->control_data, data, 3) == 3) {
+		unsigned int val;
+		i2c_master_send(codec->control_data, data, 1);
+		i2c_master_recv(codec->control_data, data, 2);
+		val = (data[0]<<8) | data[1];
+		if (val != value) {
+			pr_debug("uda1380: READ BACK VAL %x\n",
+					(data[0]<<8) | data[1]);
+			return -EIO;
+		}
+		return 0;
+	} else
+		return -EIO;
+}
+
+#define uda1380_reset(c)	uda1380_write(c, UDA1380_RESET, 0)
+
+/* declarations of ALSA reg_elem_REAL controls */
+static const char *uda1380_deemp[] = {
+	"None",
+	"32kHz",
+	"44.1kHz",
+	"48kHz",
+	"96kHz",
+};
+static const char *uda1380_input_sel[] = {
+	"Line",
+	"Mic + Line R",
+	"Line L",
+	"Mic",
+};
+static const char *uda1380_output_sel[] = {
+	"DAC",
+	"Analog Mixer",
+};
+static const char *uda1380_spf_mode[] = {
+	"Flat",
+	"Minimum1",
+	"Minimum2",
+	"Maximum"
+};
+static const char *uda1380_capture_sel[] = {
+	"ADC",
+	"Digital Mixer"
+};
+static const char *uda1380_sel_ns[] = {
+	"3rd-order",
+	"5th-order"
+};
+static const char *uda1380_mix_control[] = {
+	"off",
+	"PCM only",
+	"before sound processing",
+	"after sound processing"
+};
+static const char *uda1380_sdet_setting[] = {
+	"3200",
+	"4800",
+	"9600",
+	"19200"
+};
+static const char *uda1380_os_setting[] = {
+	"single-speed",
+	"double-speed (no mixing)",
+	"quad-speed (no mixing)"
+};
+
+static const struct soc_enum uda1380_deemp_enum[] = {
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp),
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp),
+};
+static const struct soc_enum uda1380_input_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel);		/* SEL_MIC, SEL_LNA */
+static const struct soc_enum uda1380_output_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel);		/* R02_EN_AVC */
+static const struct soc_enum uda1380_spf_enum =
+	SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode);		/* M */
+static const struct soc_enum uda1380_capture_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel);	/* SEL_SOURCE */
+static const struct soc_enum uda1380_sel_ns_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns);		/* SEL_NS */
+static const struct soc_enum uda1380_mix_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control);	/* MIX, MIX_POS */
+static const struct soc_enum uda1380_sdet_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting);	/* SD_VALUE */
+static const struct soc_enum uda1380_os_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting);	/* OS */
+
+/*
+ * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);
+
+/*
+ * from -78 dB in 1 dB steps (3 dB steps, really. LSB are ignored),
+ * from -66 dB in 0.5 dB steps (2 dB steps, really) and
+ * from -52 dB in 0.25 dB steps
+ */
+static const unsigned int mvol_tlv[] = {
+	TLV_DB_RANGE_HEAD(3),
+	0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1),
+	16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0),
+	44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0),
+};
+
+/*
+ * from -72 dB in 1.5 dB steps (6 dB steps really),
+ * from -66 dB in 0.75 dB steps (3 dB steps really),
+ * from -60 dB in 0.5 dB steps (2 dB steps really) and
+ * from -46 dB in 0.25 dB steps
+ */
+static const unsigned int vc_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1),
+	8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0),
+	16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0),
+	44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0),
+};
+
+/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */
+static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);
+
+/* from 0 to 24 dB in 2 dB steps, if SPF mode == maximum, otherwise cuts
+ * off at 18 dB max) */
+static DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0);
+
+/* from -63 to 24 dB in 0.5 dB steps (-128...48) */
+static DECLARE_TLV_DB_SCALE(dec_tlv, -6400, 50, 1);
+
+/* from 0 to 24 dB in 3 dB steps */
+static DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+
+/* from 0 to 30 dB in 2 dB steps */
+static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);
+
+static const struct snd_kcontrol_new uda1380_snd_controls[] = {
+	SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv),	/* AVCR, AVCL */
+	SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv),	/* MVCL, MVCR */
+	SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv),	/* VC2 */
+	SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv),	/* VC1 */
+	SOC_ENUM("Sound Processing Filter", uda1380_spf_enum),				/* M */
+	SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), 	/* TRL, TRR */
+	SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv),	/* BBL, BBR */
+/**/	SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1),		/* MTM */
+	SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1),		/* MT2 from decimation filter */
+	SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]),		/* DE2 */
+	SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1),		/* MT1, from digital data input */
+	SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]),		/* DE1 */
+	SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0),	/* DA_POL_INV */
+	SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum),				/* SEL_NS */
+	SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum),		/* MIX_POS, MIX */
+	SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0),			/* SILENCE, force DAC output to silence */
+	SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0),		/* SDET_ON */
+	SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum),		/* SD_VALUE */
+	SOC_ENUM("Oversampling Input", uda1380_os_enum),			/* OS */
+	SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv),	/* ML_DEC, MR_DEC */
+/**/	SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1),		/* MT_ADC */
+	SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */
+	SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0),	/* ADCPOL_INV */
+	SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv),	/* VGA_CTRL */
+	SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0),		/* SKIP_DCFIL (before decimator) */
+	SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0),		/* EN_DCFIL (at output of decimator) */
+	SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0),			/* TODO: enum, see table 62 */
+	SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1),			/* AGC_LEVEL */
+	/* -5.5, -8, -11.5, -14 dBFS */
+	SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
+};
+
+/* add non dapm controls */
+static int uda1380_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+			snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Input mux */
+static const struct snd_kcontrol_new uda1380_input_mux_control =
+	SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
+
+/* Output mux */
+static const struct snd_kcontrol_new uda1380_output_mux_control =
+	SOC_DAPM_ENUM("Route", uda1380_output_sel_enum);
+
+/* Capture mux */
+static const struct snd_kcontrol_new uda1380_capture_mux_control =
+	SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum);
+
+
+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
+	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+		&uda1380_input_mux_control),
+	SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
+		&uda1380_output_mux_control),
+	SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+		&uda1380_capture_mux_control),
+	SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
+	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
+	SND_SOC_DAPM_INPUT("VINM"),
+	SND_SOC_DAPM_INPUT("VINL"),
+	SND_SOC_DAPM_INPUT("VINR"),
+	SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("VOUTLHP"),
+	SND_SOC_DAPM_OUTPUT("VOUTRHP"),
+	SND_SOC_DAPM_OUTPUT("VOUTL"),
+	SND_SOC_DAPM_OUTPUT("VOUTR"),
+	SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
+	SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+	/* output mux */
+	{"HeadPhone Driver", NULL, "Output Mux"},
+	{"VOUTR", NULL, "Output Mux"},
+	{"VOUTL", NULL, "Output Mux"},
+
+	{"Analog Mixer", NULL, "VINR"},
+	{"Analog Mixer", NULL, "VINL"},
+	{"Analog Mixer", NULL, "DAC"},
+
+	{"Output Mux", "DAC", "DAC"},
+	{"Output Mux", "Analog Mixer", "Analog Mixer"},
+
+	/* {"DAC", "Digital Mixer", "I2S" } */
+
+	/* headphone driver */
+	{"VOUTLHP", NULL, "HeadPhone Driver"},
+	{"VOUTRHP", NULL, "HeadPhone Driver"},
+
+	/* input mux */
+	{"Left ADC", NULL, "Input Mux"},
+	{"Input Mux", "Mic", "Mic LNA"},
+	{"Input Mux", "Mic + Line R", "Mic LNA"},
+	{"Input Mux", "Line L", "Left PGA"},
+	{"Input Mux", "Line", "Left PGA"},
+
+	/* right input */
+	{"Right ADC", "Mic + Line R", "Right PGA"},
+	{"Right ADC", "Line", "Right PGA"},
+
+	/* inputs */
+	{"Mic LNA", NULL, "VINM"},
+	{"Left PGA", NULL, "VINL"},
+	{"Right PGA", NULL, "VINR"},
+};
+
+static int uda1380_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
+				  ARRAY_SIZE(uda1380_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int iface;
+
+	/* set up DAI based upon fmt */
+	iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+	iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
+
+	/* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= R01_SFORI_I2S | R01_SFORO_I2S;
+		break;
+	case SND_SOC_DAIFMT_LSB:
+		iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
+		break;
+	case SND_SOC_DAIFMT_MSB:
+		iface |= R01_SFORI_MSB | R01_SFORO_I2S;
+	}
+
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
+		iface |= R01_SIM;
+
+	uda1380_write(codec, UDA1380_IFACE, iface);
+
+	return 0;
+}
+
+/*
+ * Flush reg cache
+ * We can only write the interpolator and decimator registers
+ * when the DAI is being clocked by the CPU DAI. It's up to the
+ * machine and cpu DAI driver to do this before we are called.
+ */
+static int uda1380_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;
+	int reg, reg_start, reg_end, clk;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		reg_start = UDA1380_MVOL;
+		reg_end = UDA1380_MIXER;
+	} else {
+		reg_start = UDA1380_DEC;
+		reg_end = UDA1380_AGC;
+	}
+
+	/* FIXME disable DAC_CLK */
+	clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+	uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
+
+	for (reg = reg_start; reg <= reg_end; reg++) {
+		pr_debug("uda1380: flush reg %x val %x:", reg,
+				uda1380_read_reg_cache(codec, reg));
+		uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
+	}
+
+	/* FIXME enable DAC_CLK */
+	uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
+
+	return 0;
+}
+
+static int uda1380_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;
+	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+	/* set WSPLL power and divider if running from this clock */
+	if (clk & R00_DAC_CLK) {
+		int rate = params_rate(params);
+		u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+		clk &= ~0x3; /* clear SEL_LOOP_DIV */
+		switch (rate) {
+		case 6250 ... 12500:
+			clk |= 0x0;
+			break;
+		case 12501 ... 25000:
+			clk |= 0x1;
+			break;
+		case 25001 ... 50000:
+			clk |= 0x2;
+			break;
+		case 50001 ... 100000:
+			clk |= 0x3;
+			break;
+		}
+		uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		clk |= R00_EN_DAC | R00_EN_INT;
+	else
+		clk |= R00_EN_ADC | R00_EN_DEC;
+
+	uda1380_write(codec, UDA1380_CLK, clk);
+	return 0;
+}
+
+static void uda1380_pcm_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;
+	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+	/* shut down WSPLL power if running from this clock */
+	if (clk & R00_DAC_CLK) {
+		u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+		uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		clk &= ~(R00_EN_DAC | R00_EN_INT);
+	else
+		clk &= ~(R00_EN_ADC | R00_EN_DEC);
+
+	uda1380_write(codec, UDA1380_CLK, clk);
+}
+
+static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
+
+	/* FIXME: mute(codec,0) is called when the magician clock is already
+	 * set to WSPLL, but for some unknown reason writing to interpolator
+	 * registers works only when clocked by SYSCLK */
+	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+	uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
+	if (mute)
+		uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
+	else
+		uda1380_write(codec, UDA1380_DEEMP, mute_reg);
+	uda1380_write(codec, UDA1380_CLK, clk);
+	return 0;
+}
+
+static int uda1380_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
+		break;
+	case SND_SOC_BIAS_OFF:
+		uda1380_write(codec, UDA1380_PM, 0x0);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		       SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+		       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai uda1380_dai[] = {
+{
+	.name = "UDA1380",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = uda1380_pcm_hw_params,
+		.shutdown = uda1380_pcm_shutdown,
+		.prepare = uda1380_pcm_prepare,
+	},
+	.dai_ops = {
+		.digital_mute = uda1380_mute,
+		.set_fmt = uda1380_set_dai_fmt,
+	},
+},
+{ /* playback only - dual interface */
+	.name = "UDA1380",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = {
+		.hw_params = uda1380_pcm_hw_params,
+		.shutdown = uda1380_pcm_shutdown,
+		.prepare = uda1380_pcm_prepare,
+	},
+	.dai_ops = {
+		.digital_mute = uda1380_mute,
+		.set_fmt = uda1380_set_dai_fmt,
+	},
+},
+{ /* capture only - dual interface*/
+	.name = "UDA1380",
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = {
+		.hw_params = uda1380_pcm_hw_params,
+		.shutdown = uda1380_pcm_shutdown,
+		.prepare = uda1380_pcm_prepare,
+	},
+	.dai_ops = {
+		.set_fmt = uda1380_set_dai_fmt,
+	},
+},
+};
+EXPORT_SYMBOL_GPL(uda1380_dai);
+
+static int uda1380_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;
+
+	uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int uda1380_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(uda1380_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	uda1380_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the UDA1380 driver
+ * register mixer and dsp interfaces with the kernel
+ */
+static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "UDA1380";
+	codec->owner = THIS_MODULE;
+	codec->read = uda1380_read_reg_cache;
+	codec->write = uda1380_write;
+	codec->set_bias_level = uda1380_set_bias_level;
+	codec->dai = uda1380_dai;
+	codec->num_dai = ARRAY_SIZE(uda1380_dai);
+	codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
+				   GFP_KERNEL);
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+	codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
+	codec->reg_cache_step = 1;
+	uda1380_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		pr_err("uda1380: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* power on device */
+	uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	/* set clock input */
+	switch (dac_clk) {
+	case UDA1380_DAC_CLK_SYSCLK:
+		uda1380_write(codec, UDA1380_CLK, 0);
+		break;
+	case UDA1380_DAC_CLK_WSPLL:
+		uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
+		break;
+	}
+
+	/* uda1380 init */
+	uda1380_add_controls(codec);
+	uda1380_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		pr_err("uda1380: 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 *uda1380_socdev;
+
+#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)
+{
+	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) {
+		kfree(codec);
+		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) {
+		pr_err("uda1380: failed to initialise UDA1380\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int uda1380_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 uda1380_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, uda1380_codec_probe);
+}
+
+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,
+};
+
+static struct i2c_client client_template = {
+	.name =   "UDA1380",
+	.driver = &uda1380_i2c_driver,
+};
+#endif
+
+static int uda1380_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct uda1380_setup_data *setup;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	uda1380_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(&uda1380_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 uda1380_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)
+		uda1380_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(&uda1380_i2c_driver);
+#endif
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
+	.probe = 	uda1380_probe,
+	.remove = 	uda1380_remove,
+	.suspend = 	uda1380_suspend,
+	.resume =	uda1380_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+
+MODULE_AUTHOR("Giorgio Padrin");
+MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
new file mode 100644
index 0000000000000000000000000000000000000000..50c603e2c9f271a2a83f42136eaca9d1bf970e79
--- /dev/null
+++ b/sound/soc/codecs/uda1380.h
@@ -0,0 +1,89 @@
+/*
+ * Audio support for Philips UDA1380
+ *
+ * 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.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ */
+
+#ifndef _UDA1380_H
+#define _UDA1380_H
+
+#define UDA1380_CLK	0x00
+#define UDA1380_IFACE	0x01
+#define UDA1380_PM	0x02
+#define UDA1380_AMIX	0x03
+#define UDA1380_HP	0x04
+#define UDA1380_MVOL	0x10
+#define UDA1380_MIXVOL	0x11
+#define UDA1380_MODE	0x12
+#define UDA1380_DEEMP	0x13
+#define UDA1380_MIXER	0x14
+#define UDA1380_INTSTAT	0x18
+#define UDA1380_DEC	0x20
+#define UDA1380_PGA	0x21
+#define UDA1380_ADC	0x22
+#define UDA1380_AGC	0x23
+#define UDA1380_DECSTAT	0x28
+#define UDA1380_RESET	0x7f
+
+#define UDA1380_CACHEREGNUM 0x24
+
+/* Register flags */
+#define R00_EN_ADC	0x0800
+#define R00_EN_DEC	0x0400
+#define R00_EN_DAC	0x0200
+#define R00_EN_INT	0x0100
+#define R00_DAC_CLK	0x0010
+#define R01_SFORI_I2S   0x0000
+#define R01_SFORI_LSB16 0x0100
+#define R01_SFORI_LSB18 0x0200
+#define R01_SFORI_LSB20 0x0300
+#define R01_SFORI_MSB   0x0500
+#define R01_SFORI_MASK  0x0700
+#define R01_SFORO_I2S   0x0000
+#define R01_SFORO_LSB16 0x0001
+#define R01_SFORO_LSB18 0x0002
+#define R01_SFORO_LSB20 0x0003
+#define R01_SFORO_LSB24 0x0004
+#define R01_SFORO_MSB   0x0005
+#define R01_SFORO_MASK  0x0007
+#define R01_SEL_SOURCE  0x0040
+#define R01_SIM		0x0010
+#define R02_PON_PLL	0x8000
+#define R02_PON_HP	0x2000
+#define R02_PON_DAC	0x0400
+#define R02_PON_BIAS	0x0100
+#define R02_EN_AVC	0x0080
+#define R02_PON_AVC	0x0040
+#define R02_PON_LNA	0x0010
+#define R02_PON_PGAL	0x0008
+#define R02_PON_ADCL	0x0004
+#define R02_PON_PGAR	0x0002
+#define R02_PON_ADCR	0x0001
+#define R13_MTM		0x4000
+#define R14_SILENCE	0x0080
+#define R14_SDET_ON	0x0040
+#define R21_MT_ADC	0x8000
+#define R22_SEL_LNA	0x0008
+#define R22_SEL_MIC	0x0004
+#define R22_SKIP_DCFIL	0x0002
+#define R23_AGC_EN	0x0001
+
+struct uda1380_setup_data {
+	unsigned short i2c_address;
+	int            dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0
+#define UDA1380_DAC_CLK_WSPLL  1
+};
+
+#define UDA1380_DAI_DUPLEX	0 /* playback and capture on single DAI */
+#define UDA1380_DAI_PLAYBACK	1 /* playback DAI */
+#define UDA1380_DAI_CAPTURE	2 /* capture DAI */
+
+extern struct snd_soc_dai uda1380_dai[3];
+extern struct snd_soc_codec_device soc_codec_dev_uda1380;
+
+#endif /* _UDA1380_H */
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
new file mode 100644
index 0000000000000000000000000000000000000000..67325fd954472f51563b5803b799b69a119eca37
--- /dev/null
+++ b/sound/soc/codecs/wm8510.c
@@ -0,0 +1,817 @@
+/*
+ * wm8510.c  --  WM8510 ALSA Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.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 "wm8510.h"
+
+#define AUDIO_NAME "wm8510"
+#define WM8510_VERSION "0.6"
+
+struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+/*
+ * wm8510 register cache
+ * We can't read the WM8510 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0050, 0x0000, 0x0140, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00ff,
+	0x0000, 0x0000, 0x0100, 0x00ff,
+	0x0000, 0x0000, 0x012c, 0x002c,
+	0x002c, 0x002c, 0x002c, 0x0000,
+	0x0032, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0038, 0x000b, 0x0032, 0x0000,
+	0x0008, 0x000c, 0x0093, 0x00e9,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0003, 0x0010, 0x0000, 0x0000,
+	0x0000, 0x0002, 0x0001, 0x0000,
+	0x0000, 0x0000, 0x0039, 0x0000,
+	0x0001,
+};
+
+/*
+ * read wm8510 register cache
+ */
+static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg == WM8510_RESET)
+		return 0;
+	if (reg >= WM8510_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write wm8510 register cache
+ */
+static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= WM8510_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the WM8510 register space
+ */
+static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D9 WM8510 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8510_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+#define wm8510_reset(c)	wm8510_write(c, WM8510_RESET, 0)
+
+static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
+static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8510_alc[] = { "ALC", "Limiter" };
+
+static const struct soc_enum wm8510_enum[] = {
+	SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
+	SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
+	SOC_ENUM_SINGLE(WM8510_DAC,  4, 4, wm8510_deemp),
+	SOC_ENUM_SINGLE(WM8510_ALC3,  8, 2, wm8510_alc),
+};
+
+static const struct snd_kcontrol_new wm8510_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8510_enum[1]),
+SOC_ENUM("ADC Companding", wm8510_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
+
+SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
+
+SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_SINGLE("Capture Volume", WM8510_ADCVOL,  0, 127, 0),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1,  8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1,  4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1,  0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2,  4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2,  0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8510_ALC1,  8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1,  3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1,  0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2,  8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8510_ALC2,  4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8510_ALC2,  0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
+SOC_SINGLE("ALC Capture Decay", WM8510_ALC3,  4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8510_ALC3,  0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE,  3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE,  0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA,  7, 1, 0),
+SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA,  0, 63, 0),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL,  7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL,  6, 1, 1),
+SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL,  0, 63, 0),
+SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST,  8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
+};
+
+/* add non dapm controls */
+static int wm8510_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8510_snd_controls[i], codec,
+					NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_boost_controls[] = {
+SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA,  6, 1, 0),
+SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),
+SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_micpga_controls[] = {
+SOC_DAPM_SINGLE("MICP Switch", WM8510_INPUT, 0, 1, 0),
+SOC_DAPM_SINGLE("MICN Switch", WM8510_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8510_INPUT, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
+	&wm8510_speaker_mixer_controls[0],
+	ARRAY_SIZE(wm8510_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
+	&wm8510_mono_mixer_controls[0],
+	ARRAY_SIZE(wm8510_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0,
+		 &wm8510_micpga_controls[0],
+		 ARRAY_SIZE(wm8510_micpga_controls)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0,
+	&wm8510_boost_controls[0],
+	ARRAY_SIZE(wm8510_boost_controls)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Mono output mixer */
+	{"Mono Mixer", "PCM Playback Switch", "DAC"},
+	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+	/* Speaker output mixer */
+	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
+	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+	/* Outputs */
+	{"Mono Out", NULL, "Mono Mixer"},
+	{"MONOOUT", NULL, "Mono Out"},
+	{"SpkN Out", NULL, "Speaker Mixer"},
+	{"SpkP Out", NULL, "Speaker Mixer"},
+	{"SPKOUTN", NULL, "SpkN Out"},
+	{"SPKOUTP", NULL, "SpkP Out"},
+
+	/* Microphone PGA */
+	{"Mic PGA", "MICN Switch", "MICN"},
+	{"Mic PGA", "MICP Switch", "MICP"},
+	{ "Mic PGA", "AUX Switch", "Aux Input" },
+
+	/* Boost Mixer */
+	{"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+	{"Boost Mixer", "Mic Volume", "MICP"},
+	{"Boost Mixer", "Aux Volume", "Aux Input"},
+
+	{"ADC", NULL, "Boost Mixer"},
+};
+
+static int wm8510_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8510_dapm_widgets,
+				  ARRAY_SIZE(wm8510_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+struct pll_ {
+	unsigned int pre_div:4; /* prescale - 1 */
+	unsigned int n:4;
+	unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+	unsigned long long Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div.pre_div = 1;
+		Ndiv = target / source;
+	} else
+		pll_div.pre_div = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+			"WM8510 N value %d outwith recommended range!d\n",
+			Ndiv);
+
+	pll_div.n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div.k = K;
+}
+
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	if (freq_in == 0 || freq_out == 0) {
+		/* Clock CODEC directly from MCLK */
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+		wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+
+		/* Turn off PLL */
+		reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+		wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
+		return 0;
+	}
+
+	pll_factors(freq_out*8, freq_in);
+
+	wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+	wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+	wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+	wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+	reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+	wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+
+	/* Run CODEC from PLL instead of MCLK */
+	reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+	wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
+
+	return 0;
+}
+
+/*
+ * Configure WM8510 clock dividers.
+ */
+static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+		int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	switch (div_id) {
+	case WM8510_OPCLKDIV:
+		reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
+		wm8510_write(codec, WM8510_GPIO, reg | div);
+		break;
+	case WM8510_MCLKDIV:
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
+		wm8510_write(codec, WM8510_CLOCK, reg | div);
+		break;
+	case WM8510_ADCCLK:
+		reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
+		wm8510_write(codec, WM8510_ADC, reg | div);
+		break;
+	case WM8510_DACCLK:
+		reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
+		wm8510_write(codec, WM8510_DAC, reg | div);
+		break;
+	case WM8510_BCLKDIV:
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
+		wm8510_write(codec, WM8510_CLOCK, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+	u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		clk |= 0x0001;
+		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 |= 0x0010;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0008;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x00018;
+		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 |= 0x0180;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0100;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0080;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8510_write(codec, WM8510_IFACE, iface);
+	wm8510_write(codec, WM8510_CLOCK, clk);
+	return 0;
+}
+
+static int wm8510_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;
+	u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
+	u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0020;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0040;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x0060;
+		break;
+	}
+
+	/* filter coefficient */
+	switch (params_rate(params)) {
+	case SNDRV_PCM_RATE_8000:
+		adn |= 0x5 << 1;
+		break;
+	case SNDRV_PCM_RATE_11025:
+		adn |= 0x4 << 1;
+		break;
+	case SNDRV_PCM_RATE_16000:
+		adn |= 0x3 << 1;
+		break;
+	case SNDRV_PCM_RATE_22050:
+		adn |= 0x2 << 1;
+		break;
+	case SNDRV_PCM_RATE_32000:
+		adn |= 0x1 << 1;
+		break;
+	case SNDRV_PCM_RATE_44100:
+	case SNDRV_PCM_RATE_48000:
+		break;
+	}
+
+	wm8510_write(codec, WM8510_IFACE, iface);
+	wm8510_write(codec, WM8510_ADD, adn);
+	return 0;
+}
+
+static int wm8510_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
+
+	if (mute)
+		wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
+	else
+		wm8510_write(codec, WM8510_DAC, mute_reg);
+	return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8510_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		wm8510_write(codec, WM8510_POWER1, 0x1ff);
+		wm8510_write(codec, WM8510_POWER2, 0x1ff);
+		wm8510_write(codec, WM8510_POWER3, 0x1ff);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* everything off, dac mute, inactive */
+		wm8510_write(codec, WM8510_POWER1, 0x0);
+		wm8510_write(codec, WM8510_POWER2, 0x0);
+		wm8510_write(codec, WM8510_POWER3, 0x0);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8510_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 WM8510_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 wm8510_dai = {
+	.name = "WM8510 HiFi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8510_RATES,
+		.formats = WM8510_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8510_RATES,
+		.formats = WM8510_FORMATS,},
+	.ops = {
+		.hw_params = wm8510_pcm_hw_params,
+	},
+	.dai_ops = {
+		.digital_mute = wm8510_mute,
+		.set_fmt = wm8510_set_dai_fmt,
+		.set_clkdiv = wm8510_set_dai_clkdiv,
+		.set_pll = wm8510_set_dai_pll,
+	},
+};
+EXPORT_SYMBOL_GPL(wm8510_dai);
+
+static int wm8510_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;
+
+	wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8510_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(wm8510_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8510_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the WM8510 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8510_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "WM8510";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8510_read_reg_cache;
+	codec->write = wm8510_write;
+	codec->set_bias_level = wm8510_set_bias_level;
+	codec->dai = &wm8510_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(wm8510_reg);
+	codec->reg_cache = kmemdup(wm8510_reg, sizeof(wm8510_reg), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	wm8510_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8510: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* power on device */
+	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8510_add_controls(codec);
+	wm8510_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8510: 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 *wm8510_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * 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)
+{
+	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) {
+		kfree(codec);
+		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) {
+		pr_err("failed to initialise WM8510\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int wm8510_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 wm8510_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8510_codec_probe);
+}
+
+/* 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,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8510",
+	.driver = &wm8510_i2c_driver,
+};
+#endif
+
+static int wm8510_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8510_setup_data *setup;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	pr_info("WM8510 Audio Codec %s", WM8510_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	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");
+	}
+#else
+	/* Add other interfaces here */
+#endif
+	return ret;
+}
+
+/* power down chip */
+static int wm8510_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)
+		wm8510_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(&wm8510_i2c_driver);
+#endif
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8510 = {
+	.probe = 	wm8510_probe,
+	.remove = 	wm8510_remove,
+	.suspend = 	wm8510_suspend,
+	.resume =	wm8510_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
+
+MODULE_DESCRIPTION("ASoC WM8510 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5d2e42eb3f491730dc8fb2b46f4f7764dacc6c9
--- /dev/null
+++ b/sound/soc/codecs/wm8510.h
@@ -0,0 +1,103 @@
+/*
+ * wm8510.h  --  WM8510 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 _WM8510_H
+#define _WM8510_H
+
+/* WM8510 register space */
+
+#define WM8510_RESET		0x0
+#define WM8510_POWER1		0x1
+#define WM8510_POWER2		0x2
+#define WM8510_POWER3		0x3
+#define WM8510_IFACE		0x4
+#define WM8510_COMP			0x5
+#define WM8510_CLOCK		0x6
+#define WM8510_ADD			0x7
+#define WM8510_GPIO			0x8
+#define WM8510_DAC			0xa
+#define WM8510_DACVOL		0xb
+#define WM8510_ADC			0xe
+#define WM8510_ADCVOL		0xf
+#define WM8510_EQ1			0x12
+#define WM8510_EQ2			0x13
+#define WM8510_EQ3			0x14
+#define WM8510_EQ4			0x15
+#define WM8510_EQ5			0x16
+#define WM8510_DACLIM1		0x18
+#define WM8510_DACLIM2		0x19
+#define WM8510_NOTCH1		0x1b
+#define WM8510_NOTCH2		0x1c
+#define WM8510_NOTCH3		0x1d
+#define WM8510_NOTCH4		0x1e
+#define WM8510_ALC1			0x20
+#define WM8510_ALC2			0x21
+#define WM8510_ALC3			0x22
+#define WM8510_NGATE		0x23
+#define WM8510_PLLN			0x24
+#define WM8510_PLLK1		0x25
+#define WM8510_PLLK2		0x26
+#define WM8510_PLLK3		0x27
+#define WM8510_ATTEN		0x28
+#define WM8510_INPUT		0x2c
+#define WM8510_INPPGA		0x2d
+#define WM8510_ADCBOOST		0x2f
+#define WM8510_OUTPUT		0x31
+#define WM8510_SPKMIX		0x32
+#define WM8510_SPKVOL		0x36
+#define WM8510_MONOMIX		0x38
+
+#define WM8510_CACHEREGNUM 	57
+
+/* Clock divider Id's */
+#define WM8510_OPCLKDIV		0
+#define WM8510_MCLKDIV		1
+#define WM8510_ADCCLK		2
+#define WM8510_DACCLK		3
+#define WM8510_BCLKDIV		4
+
+/* DAC clock dividers */
+#define WM8510_DACCLK_F2	(1 << 3)
+#define WM8510_DACCLK_F4	(0 << 3)
+
+/* ADC clock dividers */
+#define WM8510_ADCCLK_F2	(1 << 3)
+#define WM8510_ADCCLK_F4	(0 << 3)
+
+/* PLL Out dividers */
+#define WM8510_OPCLKDIV_1	(0 << 4)
+#define WM8510_OPCLKDIV_2	(1 << 4)
+#define WM8510_OPCLKDIV_3	(2 << 4)
+#define WM8510_OPCLKDIV_4	(3 << 4)
+
+/* BCLK clock dividers */
+#define WM8510_BCLKDIV_1	(0 << 2)
+#define WM8510_BCLKDIV_2	(1 << 2)
+#define WM8510_BCLKDIV_4	(2 << 2)
+#define WM8510_BCLKDIV_8	(3 << 2)
+#define WM8510_BCLKDIV_16	(4 << 2)
+#define WM8510_BCLKDIV_32	(5 << 2)
+
+/* MCLK clock dividers */
+#define WM8510_MCLKDIV_1	(0 << 5)
+#define WM8510_MCLKDIV_1_5	(1 << 5)
+#define WM8510_MCLKDIV_2	(2 << 5)
+#define WM8510_MCLKDIV_3	(3 << 5)
+#define WM8510_MCLKDIV_4	(4 << 5)
+#define WM8510_MCLKDIV_6	(5 << 5)
+#define WM8510_MCLKDIV_8	(6 << 5)
+#define WM8510_MCLKDIV_12	(7 << 5)
+
+struct wm8510_setup_data {
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8510_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 0cf9265fca8fb54ef9c66d1704a896738b60d227..369d39c3f745d9d441ff23d253afaa51dc36aefb 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -31,25 +31,6 @@
 #define AUDIO_NAME "wm8731"
 #define WM8731_VERSION "0.13"
 
-/*
- * Debug
- */
-
-#define WM8731_DEBUG 0
-
-#ifdef WM8731_DEBUG
-#define dbg(format, arg...) \
-	printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
-	printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
-	printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
-	printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
 struct snd_soc_codec_device soc_codec_dev_wm8731;
 
 /* codec private data */
@@ -193,7 +174,7 @@ SND_SOC_DAPM_INPUT("RLINEIN"),
 SND_SOC_DAPM_INPUT("LLINEIN"),
 };
 
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
 	/* output mixer */
 	{"Output Mixer", "Line Bypass Switch", "Line Input"},
 	{"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -214,22 +195,14 @@ static const char *intercon[][3] = {
 	{"Line Input", NULL, "LLINEIN"},
 	{"Line Input", NULL, "RLINEIN"},
 	{"Mic Bias", NULL, "MICIN"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 static int wm8731_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+				  ARRAY_SIZE(wm8731_dapm_widgets));
 
-	/* set up audio path interconnects */
-	for (i = 0; intercon[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, intercon[i][0],
-			intercon[i][1], intercon[i][2]);
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -345,7 +318,7 @@ static void wm8731_shutdown(struct snd_pcm_substream *substream)
 	}
 }
 
-static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8731_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
@@ -357,7 +330,7 @@ static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
 	return 0;
 }
 
-static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_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;
@@ -376,7 +349,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
 }
 
 
-static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -435,29 +408,29 @@ static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 	return 0;
 }
 
-static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8731_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* vref/mid, osc on, dac unmute */
 		wm8731_write(codec, WM8731_PWR, reg);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* everything off except vref/vmid, */
 		wm8731_write(codec, WM8731_PWR, reg | 0x0040);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		/* everything off, dac mute, inactive */
 		wm8731_write(codec, WM8731_ACTIVE, 0x0);
 		wm8731_write(codec, WM8731_PWR, 0xffff);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -470,7 +443,7 @@ static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
 #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 	SNDRV_PCM_FMTBIT_S24_LE)
 
-struct snd_soc_codec_dai wm8731_dai = {
+struct snd_soc_dai wm8731_dai = {
 	.name = "WM8731",
 	.playback = {
 		.stream_name = "Playback",
@@ -503,7 +476,7 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
 	struct snd_soc_codec *codec = socdev->codec;
 
 	wm8731_write(codec, WM8731_ACTIVE, 0x0);
-	wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -521,8 +494,8 @@ static int wm8731_resume(struct platform_device *pdev)
 		data[1] = cache[i] & 0x00ff;
 		codec->hw_write(codec->control_data, data, 2);
 	}
-	wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-	wm8731_dapm_event(codec, codec->suspend_dapm_state);
+	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8731_set_bias_level(codec, codec->suspend_bias_level);
 	return 0;
 }
 
@@ -539,10 +512,10 @@ static int wm8731_init(struct snd_soc_device *socdev)
 	codec->owner = THIS_MODULE;
 	codec->read = wm8731_read_reg_cache;
 	codec->write = wm8731_write;
-	codec->dapm_event = wm8731_dapm_event;
+	codec->set_bias_level = wm8731_set_bias_level;
 	codec->dai = &wm8731_dai;
 	codec->num_dai = 1;
-	codec->reg_cache_size = sizeof(wm8731_reg);
+	codec->reg_cache_size = ARRAY_SIZE(wm8731_reg);
 	codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
@@ -557,7 +530,7 @@ static int wm8731_init(struct snd_soc_device *socdev)
 	}
 
 	/* power on device */
-	wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* set the update bits */
 	reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
@@ -632,13 +605,13 @@ static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind)
 
 	ret = i2c_attach_client(i2c);
 	if (ret < 0) {
-		err("failed to attach codec at addr %x\n", addr);
+		pr_err("failed to attach codec at addr %x\n", addr);
 		goto err;
 	}
 
 	ret = wm8731_init(socdev);
 	if (ret < 0) {
-		err("failed to initialise WM8731\n");
+		pr_err("failed to initialise WM8731\n");
 		goto err;
 	}
 	return ret;
@@ -689,7 +662,7 @@ static int wm8731_probe(struct platform_device *pdev)
 	struct wm8731_priv *wm8731;
 	int ret = 0;
 
-	info("WM8731 Audio Codec %s", WM8731_VERSION);
+	pr_info("WM8731 Audio Codec %s", WM8731_VERSION);
 
 	setup = socdev->codec_data;
 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -730,7 +703,7 @@ static int wm8731_remove(struct platform_device *pdev)
 	struct snd_soc_codec *codec = socdev->codec;
 
 	if (codec->control_data)
-		wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+		wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index 5bcab6a7afb4f8c2a760637383201944c87da565..99f2e3c60e3331c6802318b636ff709fc4796518 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -38,7 +38,7 @@ struct wm8731_setup_data {
 	unsigned short i2c_address;
 };
 
-extern struct snd_soc_codec_dai wm8731_dai;
+extern struct snd_soc_dai wm8731_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8731;
 
 #endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 16cd5d4d5ad97108443d6bc1e51db727c282b5d4..e23cb09f0d1499ef76a1fd50790b14e4aef1500a 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -31,25 +31,6 @@
 #define AUDIO_NAME "WM8750"
 #define WM8750_VERSION "0.12"
 
-/*
- * Debug
- */
-
-#define WM8750_DEBUG 0
-
-#ifdef WM8750_DEBUG
-#define dbg(format, arg...) \
-	printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
-	printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
-	printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
-	printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
 /* codec private data */
 struct wm8750_priv {
 	unsigned int sysclk;
@@ -378,7 +359,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
 	SND_SOC_DAPM_INPUT("RINPUT3"),
 };
 
-static const char *audio_map[][3] = {
+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"},
@@ -470,22 +451,14 @@ static const char *audio_map[][3] = {
 	/* ADC */
 	{"Left ADC", NULL, "Left ADC Mux"},
 	{"Right ADC", NULL, "Right ADC Mux"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 static int wm8750_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+				  ARRAY_SIZE(wm8750_dapm_widgets));
 
-	/* set up audio path audio_mapnects */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -563,7 +536,7 @@ static inline int get_coeff(int mclk, int rate)
 	return -EINVAL;
 }
 
-static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_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;
@@ -581,7 +554,7 @@ static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
 	return -EINVAL;
 }
 
-static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -674,7 +647,7 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8750_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
@@ -686,29 +659,29 @@ static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
 	return 0;
 }
 
-static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8750_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* set vmid to 50k and unmute dac */
 		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		/* set vmid to 5k for quick power up */
 		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* mute dac and set vmid to 500k, enable VREF */
 		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		wm8750_write(codec, WM8750_PWR1, 0x0001);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -719,7 +692,7 @@ static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
 #define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 	SNDRV_PCM_FMTBIT_S24_LE)
 
-struct snd_soc_codec_dai wm8750_dai = {
+struct snd_soc_dai wm8750_dai = {
 	.name = "WM8750",
 	.playback = {
 		.stream_name = "Playback",
@@ -748,7 +721,7 @@ static void wm8750_work(struct work_struct *work)
 {
 	struct snd_soc_codec *codec =
 		container_of(work, struct snd_soc_codec, delayed_work.work);
-	wm8750_dapm_event(codec, codec->dapm_state);
+	wm8750_set_bias_level(codec, codec->bias_level);
 }
 
 static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
@@ -756,7 +729,7 @@ static int wm8750_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;
 
-	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -777,12 +750,12 @@ static int wm8750_resume(struct platform_device *pdev)
 		codec->hw_write(codec->control_data, data, 2);
 	}
 
-	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* charge wm8750 caps */
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
-		wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
-		codec->dapm_state = SNDRV_CTL_POWER_D0;
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+		wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+		codec->bias_level = SND_SOC_BIAS_ON;
 		schedule_delayed_work(&codec->delayed_work,
 					msecs_to_jiffies(1000));
 	}
@@ -803,10 +776,10 @@ static int wm8750_init(struct snd_soc_device *socdev)
 	codec->owner = THIS_MODULE;
 	codec->read = wm8750_read_reg_cache;
 	codec->write = wm8750_write;
-	codec->dapm_event = wm8750_dapm_event;
+	codec->set_bias_level = wm8750_set_bias_level;
 	codec->dai = &wm8750_dai;
 	codec->num_dai = 1;
-	codec->reg_cache_size = sizeof(wm8750_reg);
+	codec->reg_cache_size = ARRAY_SIZE(wm8750_reg);
 	codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
@@ -821,8 +794,8 @@ static int wm8750_init(struct snd_soc_device *socdev)
 	}
 
 	/* charge output caps */
-	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
-	codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+	wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+	codec->bias_level = SND_SOC_BIAS_STANDBY;
 	schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
 
 	/* set the update bits */
@@ -904,13 +877,13 @@ static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind)
 
 	ret = i2c_attach_client(i2c);
 	if (ret < 0) {
-		err("failed to attach codec at addr %x\n", addr);
+		pr_err("failed to attach codec at addr %x\n", addr);
 		goto err;
 	}
 
 	ret = wm8750_init(socdev);
 	if (ret < 0) {
-	err("failed to initialise WM8750\n");
+		pr_err("failed to initialise WM8750\n");
 		goto err;
 	}
 	return ret;
@@ -961,7 +934,7 @@ static int wm8750_probe(struct platform_device *pdev)
 	struct wm8750_priv *wm8750;
 	int ret = 0;
 
-	info("WM8750 Audio Codec %s", WM8750_VERSION);
+	pr_info("WM8750 Audio Codec %s", WM8750_VERSION);
 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
 	if (codec == NULL)
 		return -ENOMEM;
@@ -1021,7 +994,7 @@ static int wm8750_remove(struct platform_device *pdev)
 	struct snd_soc_codec *codec = socdev->codec;
 
 	if (codec->control_data)
-		wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+		wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	run_delayed_work(&codec->delayed_work);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
index a97a54a6348ee492c7e2c4b7f0f35b7761ab8db0..8ef30e628b21963a35d10668747f7b76874a4192 100644
--- a/sound/soc/codecs/wm8750.h
+++ b/sound/soc/codecs/wm8750.h
@@ -61,7 +61,7 @@ struct wm8750_setup_data {
 	unsigned short i2c_address;
 };
 
-extern struct snd_soc_codec_dai wm8750_dai;
+extern struct snd_soc_dai wm8750_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8750;
 
 #endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index fb41826c4c4cdba27f39486eab75f888ff08cc99..8604809f0c362b75ee150ae0292663683baebf6f 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -55,25 +55,6 @@
 #define AUDIO_NAME "wm8753"
 #define WM8753_VERSION "0.16"
 
-/*
- * Debug
- */
-
-#define WM8753_DEBUG 0
-
-#ifdef WM8753_DEBUG
-#define dbg(format, arg...) \
-	printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
-	printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
-	printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
-	printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
 static int caps_charge = 2000;
 module_param(caps_charge, int, 0);
 MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
@@ -260,28 +241,50 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
 	return 1;
 }
 
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const unsigned int out_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	/* 0000000 - 0101111 = "Analogue mute" */
+	0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0),
+	48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0),
+};
+static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
 
 static const struct snd_kcontrol_new wm8753_snd_controls[] = {
-SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
-
-SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0),
-
-SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),
-SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),
-
-SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),
-
-SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),
-SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),
-SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),
-
-SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),
-SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),
-
-SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
-SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
-SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1),
+SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv),
+
+SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0,
+		 adc_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V,
+		 0, 127, 0, out_tlv),
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0,
+		 127, 0, out_tlv),
+
+SOC_SINGLE_TLV("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0, out_tlv),
+
+SOC_DOUBLE_R_TLV("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7,
+		 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4,
+		 7, 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7,
+		 1, voice_mix_tlv),
+
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7,
+	     1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7,
+	     1, 0),
+
+SOC_SINGLE_TLV("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1, mix_tlv),
+SOC_SINGLE_TLV("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1,
+	       mix_tlv),
+SOC_SINGLE_TLV("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1,
+	       voice_mix_tlv),
 SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
 
 SOC_ENUM("Bass Boost", wm8753_enum[0]),
@@ -291,10 +294,13 @@ SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),
 SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
 SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
 
-SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv),
-SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv),
+SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1,
+	       rec_mix_tlv),
+SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1,
+	       rec_mix_tlv),
 
-SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0,
+		 pga_tlv),
 SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
 SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),
 
@@ -326,8 +332,8 @@ SOC_ENUM("De-emphasis", wm8753_enum[8]),
 SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
 SOC_ENUM("Playback Phase", wm8753_enum[10]),
 
-SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
-SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
+SOC_SINGLE_TLV("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0, mic_preamp_tlv),
+SOC_SINGLE_TLV("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0, mic_preamp_tlv),
 
 SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
 
@@ -523,7 +529,7 @@ SND_SOC_DAPM_INPUT("MIC2"),
 SND_SOC_DAPM_VMID("VREF"),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* left mixer */
 	{"Left Mixer", "Left Playback Switch", "Left DAC"},
 	{"Left Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -674,23 +680,14 @@ static const char *audio_map[][3] = {
 
 	/* ACOP */
 	{"ACOP", NULL, "ALC Mixer"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 static int wm8753_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
+	snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+				  ARRAY_SIZE(wm8753_dapm_widgets));
 
-	for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
-
-	/* set up the WM8753 audio map */
-	for (i = 0; audio_map[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
-	}
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -743,7 +740,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
 	pll_div->k = K;
 }
 
-static int wm8753_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
 		int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
 	u16 reg, enable;
@@ -866,7 +863,7 @@ static int get_coeff(int mclk, int rate)
 /*
  * Clock after PLL and dividers
  */
-static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_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;
@@ -893,7 +890,7 @@ static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
 /*
  * Set's ADC and Voice DAC format.
  */
-static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -963,7 +960,7 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
 /*
  * Set's PCM dai fmt and BCLK.
  */
-static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1029,7 +1026,7 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 	return 0;
 }
 
-static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 		int div_id, int div)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1057,7 +1054,7 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
 /*
  * Set's HiFi DAC format.
  */
-static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1090,7 +1087,7 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 /*
  * Set's I2S DAI format.
  */
-static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1198,7 +1195,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1213,7 +1210,7 @@ static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 	return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
@@ -1221,7 +1218,7 @@ static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 	return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1236,7 +1233,7 @@ static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 	return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1253,7 +1250,7 @@ static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 	return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8753_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7;
@@ -1274,29 +1271,29 @@ static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
 	return 0;
 }
 
-static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8753_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* set vmid to 50k and unmute dac */
 		wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		/* set vmid to 5k for quick power up */
 		wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* mute dac and set vmid to 500k, enable VREF */
 		wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		wm8753_write(codec, WM8753_PWR1, 0x0001);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -1319,7 +1316,7 @@ static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
  * 3. Voice disabled - HIFI over HIFI
  * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
  */
-static const struct snd_soc_codec_dai wm8753_all_dai[] = {
+static const struct snd_soc_dai wm8753_all_dai[] = {
 /* DAI HiFi mode 1 */
 {	.name = "WM8753 HiFi",
 	.id = 1,
@@ -1459,7 +1456,7 @@ static const struct snd_soc_codec_dai wm8753_all_dai[] = {
 },
 };
 
-struct snd_soc_codec_dai wm8753_dai[2];
+struct snd_soc_dai wm8753_dai[2];
 EXPORT_SYMBOL_GPL(wm8753_dai);
 
 static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
@@ -1500,7 +1497,7 @@ static void wm8753_work(struct work_struct *work)
 {
 	struct snd_soc_codec *codec =
 		container_of(work, struct snd_soc_codec, delayed_work.work);
-	wm8753_dapm_event(codec, codec->dapm_state);
+	wm8753_set_bias_level(codec, codec->bias_level);
 }
 
 static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
@@ -1512,7 +1509,7 @@ static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
 	if (!codec->card)
 		return 0;
 
-	wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -1537,12 +1534,12 @@ static int wm8753_resume(struct platform_device *pdev)
 		codec->hw_write(codec->control_data, data, 2);
 	}
 
-	wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* charge wm8753 caps */
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
-		wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
-		codec->dapm_state = SNDRV_CTL_POWER_D0;
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+		wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+		codec->bias_level = SND_SOC_BIAS_ON;
 		schedule_delayed_work(&codec->delayed_work,
 			msecs_to_jiffies(caps_charge));
 	}
@@ -1563,10 +1560,10 @@ static int wm8753_init(struct snd_soc_device *socdev)
 	codec->owner = THIS_MODULE;
 	codec->read = wm8753_read_reg_cache;
 	codec->write = wm8753_write;
-	codec->dapm_event = wm8753_dapm_event;
+	codec->set_bias_level = wm8753_set_bias_level;
 	codec->dai = wm8753_dai;
 	codec->num_dai = 2;
-	codec->reg_cache_size = sizeof(wm8753_reg);
+	codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
 	codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL);
 
 	if (codec->reg_cache == NULL)
@@ -1584,8 +1581,8 @@ static int wm8753_init(struct snd_soc_device *socdev)
 	}
 
 	/* charge output caps */
-	wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
-	codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+	wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+	codec->bias_level = SND_SOC_BIAS_STANDBY;
 	schedule_delayed_work(&codec->delayed_work,
 		msecs_to_jiffies(caps_charge));
 
@@ -1673,13 +1670,13 @@ static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind)
 
 	ret = i2c_attach_client(i2c);
 	if (ret < 0) {
-		err("failed to attach codec at addr %x\n", addr);
+		pr_err("failed to attach codec at addr %x\n", addr);
 		goto err;
 	}
 
 	ret = wm8753_init(socdev);
 	if (ret < 0) {
-		err("failed to initialise WM8753\n");
+		pr_err("failed to initialise WM8753\n");
 		goto err;
 	}
 
@@ -1731,7 +1728,7 @@ static int wm8753_probe(struct platform_device *pdev)
 	struct wm8753_priv *wm8753;
 	int ret = 0;
 
-	info("WM8753 Audio Codec %s", WM8753_VERSION);
+	pr_info("WM8753 Audio Codec %s", WM8753_VERSION);
 
 	setup = socdev->codec_data;
 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -1792,7 +1789,7 @@ static int wm8753_remove(struct platform_device *pdev)
 	struct snd_soc_codec *codec = socdev->codec;
 
 	if (codec->control_data)
-		wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+		wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	run_delayed_work(&codec->delayed_work);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index 95e2a1f53169f6ee9fd5da0e9c1ed14c7af1581f..44f5f1ff0cc7080bfb920c0c5fbb345901896e8a 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -120,7 +120,7 @@ struct wm8753_setup_data {
 #define WM8753_DAI_HIFI		0
 #define WM8753_DAI_VOICE		1
 
-extern struct snd_soc_codec_dai wm8753_dai[2];
+extern struct snd_soc_dai wm8753_dai[2];
 extern struct snd_soc_codec_device soc_codec_dev_wm8753;
 
 #endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
new file mode 100644
index 0000000000000000000000000000000000000000..3ecce5168e94aaf936c0fd774d9b7722a6c8635b
--- /dev/null
+++ b/sound/soc/codecs/wm8990.c
@@ -0,0 +1,1626 @@
+/*
+ * wm8990.c  --  WM8990 ALSA Soc Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         lg@opensource.wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.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 <asm/div64.h>
+
+#include "wm8990.h"
+
+#define AUDIO_NAME "wm8990"
+#define WM8990_VERSION "0.2"
+
+/* codec private data */
+struct wm8990_priv {
+	unsigned int sysclk;
+	unsigned int pcmclk;
+};
+
+/*
+ * wm8990 register cache.  Note that register 0 is not included in the
+ * cache.
+ */
+static const u16 wm8990_reg[] = {
+	0x8990,     /* R0  - Reset */
+	0x0000,     /* R1  - Power Management (1) */
+	0x6000,     /* R2  - Power Management (2) */
+	0x0000,     /* R3  - Power Management (3) */
+	0x4050,     /* R4  - Audio Interface (1) */
+	0x4000,     /* R5  - Audio Interface (2) */
+	0x01C8,     /* R6  - Clocking (1) */
+	0x0000,     /* R7  - Clocking (2) */
+	0x0040,     /* R8  - Audio Interface (3) */
+	0x0040,     /* R9  - Audio Interface (4) */
+	0x0004,     /* R10 - DAC CTRL */
+	0x00C0,     /* R11 - Left DAC Digital Volume */
+	0x00C0,     /* R12 - Right DAC Digital Volume */
+	0x0000,     /* R13 - Digital Side Tone */
+	0x0100,     /* R14 - ADC CTRL */
+	0x00C0,     /* R15 - Left ADC Digital Volume */
+	0x00C0,     /* R16 - Right ADC Digital Volume */
+	0x0000,     /* R17 */
+	0x0000,     /* R18 - GPIO CTRL 1 */
+	0x1000,     /* R19 - GPIO1 & GPIO2 */
+	0x1010,     /* R20 - GPIO3 & GPIO4 */
+	0x1010,     /* R21 - GPIO5 & GPIO6 */
+	0x8000,     /* R22 - GPIOCTRL 2 */
+	0x0800,     /* R23 - GPIO_POL */
+	0x008B,     /* R24 - Left Line Input 1&2 Volume */
+	0x008B,     /* R25 - Left Line Input 3&4 Volume */
+	0x008B,     /* R26 - Right Line Input 1&2 Volume */
+	0x008B,     /* R27 - Right Line Input 3&4 Volume */
+	0x0000,     /* R28 - Left Output Volume */
+	0x0000,     /* R29 - Right Output Volume */
+	0x0066,     /* R30 - Line Outputs Volume */
+	0x0022,     /* R31 - Out3/4 Volume */
+	0x0079,     /* R32 - Left OPGA Volume */
+	0x0079,     /* R33 - Right OPGA Volume */
+	0x0003,     /* R34 - Speaker Volume */
+	0x0003,     /* R35 - ClassD1 */
+	0x0000,     /* R36 */
+	0x0100,     /* R37 - ClassD3 */
+	0x0000,     /* R38 */
+	0x0000,     /* R39 - Input Mixer1 */
+	0x0000,     /* R40 - Input Mixer2 */
+	0x0000,     /* R41 - Input Mixer3 */
+	0x0000,     /* R42 - Input Mixer4 */
+	0x0000,     /* R43 - Input Mixer5 */
+	0x0000,     /* R44 - Input Mixer6 */
+	0x0000,     /* R45 - Output Mixer1 */
+	0x0000,     /* R46 - Output Mixer2 */
+	0x0000,     /* R47 - Output Mixer3 */
+	0x0000,     /* R48 - Output Mixer4 */
+	0x0000,     /* R49 - Output Mixer5 */
+	0x0000,     /* R50 - Output Mixer6 */
+	0x0180,     /* R51 - Out3/4 Mixer */
+	0x0000,     /* R52 - Line Mixer1 */
+	0x0000,     /* R53 - Line Mixer2 */
+	0x0000,     /* R54 - Speaker Mixer */
+	0x0000,     /* R55 - Additional Control */
+	0x0000,     /* R56 - AntiPOP1 */
+	0x0000,     /* R57 - AntiPOP2 */
+	0x0000,     /* R58 - MICBIAS */
+	0x0000,     /* R59 */
+	0x0008,     /* R60 - PLL1 */
+	0x0031,     /* R61 - PLL2 */
+	0x0026,     /* R62 - PLL3 */
+};
+
+/*
+ * read wm8990 register cache
+ */
+static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+	return cache[reg];
+}
+
+/*
+ * write wm8990 register cache
+ */
+static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+
+	/* Reset register is uncached */
+	if (reg == 0)
+		return;
+
+	cache[reg] = value;
+}
+
+/*
+ * write to the wm8990 register space
+ */
+static int wm8990_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[3];
+
+	data[0] = reg & 0xFF;
+	data[1] = (value >> 8) & 0xFF;
+	data[2] = value & 0xFF;
+
+	wm8990_write_reg_cache(codec, reg, value);
+
+	if (codec->hw_write(codec->control_data, data, 3) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+#define wm8990_reset(c) wm8990_write(c, WM8990_RESET, 0)
+
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+
+static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+
+static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
+
+static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+
+static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+
+static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+
+static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+
+static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+
+static int wm899x_outpga_put_volsw_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 ret;
+	u16 val;
+
+	ret = snd_soc_put_volsw(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	/* now hit the volume update bits (always bit 8) */
+	val = wm8990_read_reg_cache(codec, reg);
+	return wm8990_write(codec, reg, val | 0x0100);
+}
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+	 tlv_array) {\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		  SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
+	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+
+static const char *wm8990_digital_sidetone[] =
+	{"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8990_left_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADC_TO_DACL_SHIFT,
+	WM8990_ADC_TO_DACL_MASK,
+	wm8990_digital_sidetone);
+
+static const struct soc_enum wm8990_right_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADC_TO_DACR_SHIFT,
+	WM8990_ADC_TO_DACR_MASK,
+	wm8990_digital_sidetone);
+
+static const char *wm8990_adcmode[] =
+	{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8990_right_adcmode_enum =
+SOC_ENUM_SINGLE(WM8990_ADC_CTRL,
+	WM8990_ADC_HPF_CUT_SHIFT,
+	WM8990_ADC_HPF_CUT_MASK,
+	wm8990_adcmode);
+
+static const struct snd_kcontrol_new wm8990_snd_controls[] = {
+/* INMIXL */
+SOC_SINGLE("LIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L12MNBST_BIT, 1, 0),
+SOC_SINGLE("LIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L34MNBST_BIT, 1, 0),
+/* INMIXR */
+SOC_SINGLE("RIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R12MNBST_BIT, 1, 0),
+SOC_SINGLE("RIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R34MNBST_BIT, 1, 0),
+
+/* LOMIX */
+SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER3,
+	WM8990_LLI3LOVOL_SHIFT, WM8990_LLI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+	WM8990_LR12LOVOL_SHIFT, WM8990_LR12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+	WM8990_LL12LOVOL_SHIFT, WM8990_LL12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER5,
+	WM8990_LRI3LOVOL_SHIFT, WM8990_LRI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+	WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+	WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+/* ROMIX */
+SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER4,
+	WM8990_RRI3ROVOL_SHIFT, WM8990_RRI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+	WM8990_RL12ROVOL_SHIFT, WM8990_RL12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+	WM8990_RR12ROVOL_SHIFT, WM8990_RR12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER6,
+	WM8990_RLI3ROVOL_SHIFT, WM8990_RLI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+	WM8990_RLBROVOL_SHIFT, WM8990_RLBROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+	WM8990_RRBROVOL_SHIFT, WM8990_RRBROVOL_MASK, 1, out_mix_tlv),
+
+/* LOUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8990_LEFT_OUTPUT_VOLUME,
+	WM8990_LOUTVOL_SHIFT, WM8990_LOUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOUT ZC", WM8990_LEFT_OUTPUT_VOLUME, WM8990_LOZC_BIT, 1, 0),
+
+/* ROUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8990_RIGHT_OUTPUT_VOLUME,
+	WM8990_ROUTVOL_SHIFT, WM8990_ROUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROUT ZC", WM8990_RIGHT_OUTPUT_VOLUME, WM8990_ROZC_BIT, 1, 0),
+
+/* LOPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8990_LEFT_OPGA_VOLUME,
+	WM8990_LOPGAVOL_SHIFT, WM8990_LOPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOPGA ZC Switch", WM8990_LEFT_OPGA_VOLUME,
+	WM8990_LOPGAZC_BIT, 1, 0),
+
+/* ROPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8990_RIGHT_OPGA_VOLUME,
+	WM8990_ROPGAVOL_SHIFT, WM8990_ROPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROPGA ZC Switch", WM8990_RIGHT_OPGA_VOLUME,
+	WM8990_ROPGAZC_BIT, 1, 0),
+
+SOC_SINGLE("LON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_LONMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_LOPMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_LOATTN_BIT, 1, 0),
+SOC_SINGLE("RON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_RONMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_ROPMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_ROATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT3 Mute Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT3MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT3 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT3ATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT4 Mute Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT4MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT4 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT4ATTN_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Mode Switch", WM8990_CLASSD1,
+	WM8990_CDMODE_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Output Attenuation Volume", WM8990_SPEAKER_VOLUME,
+	WM8990_SPKVOL_SHIFT, WM8990_SPKVOL_MASK, 0),
+SOC_SINGLE("Speaker DC Boost Volume", WM8990_CLASSD3,
+	WM8990_DCGAIN_SHIFT, WM8990_DCGAIN_MASK, 0),
+SOC_SINGLE("Speaker AC Boost Volume", WM8990_CLASSD3,
+	WM8990_ACGAIN_SHIFT, WM8990_ACGAIN_MASK, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+	WM8990_LEFT_DAC_DIGITAL_VOLUME,
+	WM8990_DACL_VOL_SHIFT,
+	WM8990_DACL_VOL_MASK,
+	0,
+	out_dac_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+	WM8990_RIGHT_DAC_DIGITAL_VOLUME,
+	WM8990_DACR_VOL_SHIFT,
+	WM8990_DACR_VOL_MASK,
+	0,
+	out_dac_tlv),
+
+SOC_ENUM("Left Digital Sidetone", wm8990_left_digital_sidetone_enum),
+SOC_ENUM("Right Digital Sidetone", wm8990_right_digital_sidetone_enum),
+
+SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADCL_DAC_SVOL_SHIFT, WM8990_ADCL_DAC_SVOL_MASK, 0,
+	out_sidetone_tlv),
+SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADCR_DAC_SVOL_SHIFT, WM8990_ADCR_DAC_SVOL_MASK, 0,
+	out_sidetone_tlv),
+
+SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8990_ADC_CTRL,
+	WM8990_ADC_HPF_ENA_BIT, 1, 0),
+
+SOC_ENUM("ADC HPF Mode", wm8990_right_adcmode_enum),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+	WM8990_LEFT_ADC_DIGITAL_VOLUME,
+	WM8990_ADCL_VOL_SHIFT,
+	WM8990_ADCL_VOL_MASK,
+	0,
+	in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+	WM8990_RIGHT_ADC_DIGITAL_VOLUME,
+	WM8990_ADCR_VOL_SHIFT,
+	WM8990_ADCR_VOL_MASK,
+	0,
+	in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+	WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+	WM8990_LIN12VOL_SHIFT,
+	WM8990_LIN12VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("LIN12 ZC Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+	WM8990_LI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN12 Mute Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+	WM8990_LI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+	WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+	WM8990_LIN34VOL_SHIFT,
+	WM8990_LIN34VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("LIN34 ZC Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+	WM8990_LI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN34 Mute Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+	WM8990_LI34MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+	WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+	WM8990_RIN12VOL_SHIFT,
+	WM8990_RIN12VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("RIN12 ZC Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+	WM8990_RI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN12 Mute Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+	WM8990_RI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+	WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+	WM8990_RIN34VOL_SHIFT,
+	WM8990_RIN34VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("RIN34 ZC Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+	WM8990_RI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+	WM8990_RI34MUTE_BIT, 1, 0),
+
+};
+
+/* add non dapm controls */
+static int wm8990_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8990_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8990_snd_controls[i], codec,
+					NULL));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ * _DAPM_ Controls
+ */
+
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	u16 reg, fakepower;
+
+	reg = wm8990_read_reg_cache(w->codec, WM8990_POWER_MANAGEMENT_2);
+	fakepower = wm8990_read_reg_cache(w->codec, WM8990_INTDRIVBITS);
+
+	if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) |
+		(1 << WM8990_AINLMUX_PWR_BIT))) {
+		reg |= WM8990_AINL_ENA;
+	} else {
+		reg &= ~WM8990_AINL_ENA;
+	}
+
+	if (fakepower & ((1 << WM8990_INMIXR_PWR_BIT) |
+		(1 << WM8990_AINRMUX_PWR_BIT))) {
+		reg |= WM8990_AINR_ENA;
+	} else {
+		reg &= ~WM8990_AINL_ENA;
+	}
+	wm8990_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+	return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	u32 reg_shift = kcontrol->private_value & 0xfff;
+	int ret = 0;
+	u16 reg;
+
+	switch (reg_shift) {
+	case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
+		reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER1);
+		if (reg & WM8990_LDLO) {
+			printk(KERN_WARNING
+			"Cannot set as Output Mixer 1 LDLO Set\n");
+			ret = -1;
+		}
+		break;
+	case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
+		reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER2);
+		if (reg & WM8990_RDRO) {
+			printk(KERN_WARNING
+			"Cannot set as Output Mixer 2 RDRO Set\n");
+			ret = -1;
+		}
+		break;
+	case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
+		reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+		if (reg & WM8990_LDSPK) {
+			printk(KERN_WARNING
+			"Cannot set as Speaker Mixer LDSPK Set\n");
+			ret = -1;
+		}
+		break;
+	case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
+		reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+		if (reg & WM8990_RDSPK) {
+			printk(KERN_WARNING
+			"Cannot set as Speaker Mixer RDSPK Set\n");
+			ret = -1;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN1 Switch", WM8990_INPUT_MIXER2, WM8990_LMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN2 Switch", WM8990_INPUT_MIXER2, WM8990_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_lin34_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN3 Switch", WM8990_INPUT_MIXER2, WM8990_LMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN4 Switch", WM8990_INPUT_MIXER2, WM8990_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_rin12_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN1 Switch", WM8990_INPUT_MIXER2, WM8990_RMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN2 Switch", WM8990_INPUT_MIXER2, WM8990_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_rin34_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN3 Switch", WM8990_INPUT_MIXER2, WM8990_RMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN4 Switch", WM8990_INPUT_MIXER2, WM8990_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8990_dapm_inmixl_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8990_INPUT_MIXER3,
+	WM8990_LDBVOL_SHIFT, WM8990_LDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8990_INPUT_MIXER5, WM8990_LI2BVOL_SHIFT,
+	7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("LINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+	1, 0),
+SOC_DAPM_SINGLE("LINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+	1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8990_dapm_inmixr_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8990_INPUT_MIXER4,
+	WM8990_RDBVOL_SHIFT, WM8990_RDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8990_INPUT_MIXER6, WM8990_RI2BVOL_SHIFT,
+	7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("RINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+	1, 0),
+SOC_DAPM_SINGLE("RINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+	1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8990_ainlmux[] =
+	{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8990_ainlmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT,
+	ARRAY_SIZE(wm8990_ainlmux), wm8990_ainlmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainlmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8990_ainrmux[] =
+	{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8990_ainrmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT,
+	ARRAY_SIZE(wm8990_ainrmux), wm8990_ainrmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainrmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8990_dapm_rxvoice_controls[] = {
+SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8990_INPUT_MIXER5, WM8990_LR4BVOL_SHIFT,
+			WM8990_LR4BVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8990_INPUT_MIXER6, WM8990_RL4BVOL_SHIFT,
+			WM8990_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lomix_controls[] = {
+SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LRBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LLBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LRI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LLI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LR12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LL12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8990_dapm_romix_controls[] = {
+SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RLBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RRBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RLI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RRI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RL12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RR12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lonmix_controls[] = {
+SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+	WM8990_LLOPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER1,
+	WM8990_LROPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8990_LINE_MIXER1,
+	WM8990_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lopmix_controls[] = {
+SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER1,
+	WM8990_LR12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER1,
+	WM8990_LL12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+	WM8990_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ronmix_controls[] = {
+SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+	WM8990_RROPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER2,
+	WM8990_RLOPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8990_LINE_MIXER2,
+	WM8990_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ropmix_controls[] = {
+SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER2,
+	WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER2,
+	WM8990_RR12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+	WM8990_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out3mix_controls[] = {
+SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+	WM8990_LI4O3_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8990_OUT3_4_MIXER,
+	WM8990_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out4mix_controls[] = {
+SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8990_OUT3_4_MIXER,
+	WM8990_RPGAO4_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+	WM8990_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8990_dapm_spkmix_controls[] = {
+SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LI2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LB2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LOPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8990_SPEAKER_MIXER,
+	WM8990_RDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+	WM8990_ROPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8990_dapm_widgets[] = {
+/* Input Side */
+/* Input Lines */
+SND_SOC_DAPM_INPUT("LIN1"),
+SND_SOC_DAPM_INPUT("LIN2"),
+SND_SOC_DAPM_INPUT("LIN3"),
+SND_SOC_DAPM_INPUT("LIN4/RXN"),
+SND_SOC_DAPM_INPUT("RIN3"),
+SND_SOC_DAPM_INPUT("RIN4/RXP"),
+SND_SOC_DAPM_INPUT("RIN1"),
+SND_SOC_DAPM_INPUT("RIN2"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+/* DACs */
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2,
+	WM8990_ADCL_ENA_BIT, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8990_POWER_MANAGEMENT_2,
+	WM8990_ADCR_ENA_BIT, 0),
+
+/* Input PGAs */
+SND_SOC_DAPM_MIXER("LIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN12_ENA_BIT,
+	0, &wm8990_dapm_lin12_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lin12_pga_controls)),
+SND_SOC_DAPM_MIXER("LIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN34_ENA_BIT,
+	0, &wm8990_dapm_lin34_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lin34_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN12_ENA_BIT,
+	0, &wm8990_dapm_rin12_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_rin12_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT,
+	0, &wm8990_dapm_rin34_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)),
+
+/* INMIXL */
+SND_SOC_DAPM_MIXER_E("INMIXL", WM8990_INTDRIVBITS, WM8990_INMIXL_PWR_BIT, 0,
+	&wm8990_dapm_inmixl_controls[0],
+	ARRAY_SIZE(wm8990_dapm_inmixl_controls),
+	inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINLMUX */
+SND_SOC_DAPM_MUX_E("AILNMUX", WM8990_INTDRIVBITS, WM8990_AINLMUX_PWR_BIT, 0,
+	&wm8990_dapm_ainlmux_controls, inmixer_event,
+	SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* INMIXR */
+SND_SOC_DAPM_MIXER_E("INMIXR", WM8990_INTDRIVBITS, WM8990_INMIXR_PWR_BIT, 0,
+	&wm8990_dapm_inmixr_controls[0],
+	ARRAY_SIZE(wm8990_dapm_inmixr_controls),
+	inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINRMUX */
+SND_SOC_DAPM_MUX_E("AIRNMUX", WM8990_INTDRIVBITS, WM8990_AINRMUX_PWR_BIT, 0,
+	&wm8990_dapm_ainrmux_controls, inmixer_event,
+	SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* Output Side */
+/* DACs */
+SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8990_POWER_MANAGEMENT_3,
+	WM8990_DACL_ENA_BIT, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8990_POWER_MANAGEMENT_3,
+	WM8990_DACR_ENA_BIT, 0),
+
+/* LOMIX */
+SND_SOC_DAPM_MIXER_E("LOMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOMIX_ENA_BIT,
+	0, &wm8990_dapm_lomix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lomix_controls),
+	outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LONMIX */
+SND_SOC_DAPM_MIXER("LONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LON_ENA_BIT, 0,
+	&wm8990_dapm_lonmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lonmix_controls)),
+
+/* LOPMIX */
+SND_SOC_DAPM_MIXER("LOPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOP_ENA_BIT, 0,
+	&wm8990_dapm_lopmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lopmix_controls)),
+
+/* OUT3MIX */
+SND_SOC_DAPM_MIXER("OUT3MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT3_ENA_BIT, 0,
+	&wm8990_dapm_out3mix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_out3mix_controls)),
+
+/* SPKMIX */
+SND_SOC_DAPM_MIXER_E("SPKMIX", WM8990_POWER_MANAGEMENT_1, WM8990_SPK_ENA_BIT, 0,
+	&wm8990_dapm_spkmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_spkmix_controls), outmixer_event,
+	SND_SOC_DAPM_PRE_REG),
+
+/* OUT4MIX */
+SND_SOC_DAPM_MIXER("OUT4MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT4_ENA_BIT, 0,
+	&wm8990_dapm_out4mix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_out4mix_controls)),
+
+/* ROPMIX */
+SND_SOC_DAPM_MIXER("ROPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROP_ENA_BIT, 0,
+	&wm8990_dapm_ropmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_ropmix_controls)),
+
+/* RONMIX */
+SND_SOC_DAPM_MIXER("RONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_RON_ENA_BIT, 0,
+	&wm8990_dapm_ronmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_ronmix_controls)),
+
+/* ROMIX */
+SND_SOC_DAPM_MIXER_E("ROMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROMIX_ENA_BIT,
+	0, &wm8990_dapm_romix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_romix_controls),
+	outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LOUT PGA */
+SND_SOC_DAPM_PGA("LOUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_LOUT_ENA_BIT, 0,
+	NULL, 0),
+
+/* ROUT PGA */
+SND_SOC_DAPM_PGA("ROUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_ROUT_ENA_BIT, 0,
+	NULL, 0),
+
+/* LOPGA */
+SND_SOC_DAPM_PGA("LOPGA", WM8990_POWER_MANAGEMENT_3, WM8990_LOPGA_ENA_BIT, 0,
+	NULL, 0),
+
+/* ROPGA */
+SND_SOC_DAPM_PGA("ROPGA", WM8990_POWER_MANAGEMENT_3, WM8990_ROPGA_ENA_BIT, 0,
+	NULL, 0),
+
+/* MICBIAS */
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8990_POWER_MANAGEMENT_1,
+	WM8990_MICBIAS_ENA_BIT, 0),
+
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Make DACs turn on when playing even if not mixed into any outputs */
+	{"Internal DAC Sink", NULL, "Left DAC"},
+	{"Internal DAC Sink", NULL, "Right DAC"},
+
+	/* Make ADCs turn on when recording even if not mixed from any inputs */
+	{"Left ADC", NULL, "Internal ADC Source"},
+	{"Right ADC", NULL, "Internal ADC Source"},
+
+	/* Input Side */
+	/* LIN12 PGA */
+	{"LIN12 PGA", "LIN1 Switch", "LIN1"},
+	{"LIN12 PGA", "LIN2 Switch", "LIN2"},
+	/* LIN34 PGA */
+	{"LIN34 PGA", "LIN3 Switch", "LIN3"},
+	{"LIN34 PGA", "LIN4 Switch", "LIN4"},
+	/* INMIXL */
+	{"INMIXL", "Record Left Volume", "LOMIX"},
+	{"INMIXL", "LIN2 Volume", "LIN2"},
+	{"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+	{"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+	/* AILNMUX */
+	{"AILNMUX", "INMIXL Mix", "INMIXL"},
+	{"AILNMUX", "DIFFINL Mix", "LIN12PGA"},
+	{"AILNMUX", "DIFFINL Mix", "LIN34PGA"},
+	{"AILNMUX", "RXVOICE Mix", "LIN4/RXN"},
+	{"AILNMUX", "RXVOICE Mix", "RIN4/RXP"},
+	/* ADC */
+	{"Left ADC", NULL, "AILNMUX"},
+
+	/* RIN12 PGA */
+	{"RIN12 PGA", "RIN1 Switch", "RIN1"},
+	{"RIN12 PGA", "RIN2 Switch", "RIN2"},
+	/* RIN34 PGA */
+	{"RIN34 PGA", "RIN3 Switch", "RIN3"},
+	{"RIN34 PGA", "RIN4 Switch", "RIN4"},
+	/* INMIXL */
+	{"INMIXR", "Record Right Volume", "ROMIX"},
+	{"INMIXR", "RIN2 Volume", "RIN2"},
+	{"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+	{"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+	/* AIRNMUX */
+	{"AIRNMUX", "INMIXR Mix", "INMIXR"},
+	{"AIRNMUX", "DIFFINR Mix", "RIN12PGA"},
+	{"AIRNMUX", "DIFFINR Mix", "RIN34PGA"},
+	{"AIRNMUX", "RXVOICE Mix", "RIN4/RXN"},
+	{"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"},
+	/* ADC */
+	{"Right ADC", NULL, "AIRNMUX"},
+
+	/* LOMIX */
+	{"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+	{"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+	{"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+	/* ROMIX */
+	{"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+	{"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+	{"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+	/* SPKMIX */
+	{"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+	{"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+	{"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+	{"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+	{"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+	{"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+	{"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+	{"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+	/* LONMIX */
+	{"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+	/* LOPMIX */
+	{"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+	/* OUT3MIX */
+	{"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXP"},
+	{"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+	/* OUT4MIX */
+	{"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+	{"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
+
+	/* RONMIX */
+	{"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+	/* ROPMIX */
+	{"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+	/* Out Mixer PGAs */
+	{"LOPGA", NULL, "LOMIX"},
+	{"ROPGA", NULL, "ROMIX"},
+
+	{"LOUT PGA", NULL, "LOMIX"},
+	{"ROUT PGA", NULL, "ROMIX"},
+
+	/* Output Pins */
+	{"LON", NULL, "LONMIX"},
+	{"LOP", NULL, "LOPMIX"},
+	{"OUT", NULL, "OUT3MIX"},
+	{"LOUT", NULL, "LOUT PGA"},
+	{"SPKN", NULL, "SPKMIX"},
+	{"ROUT", NULL, "ROUT PGA"},
+	{"OUT4", NULL, "OUT4MIX"},
+	{"ROP", NULL, "ROPMIX"},
+	{"RON", NULL, "RONMIX"},
+};
+
+static int wm8990_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8990_dapm_widgets,
+				  ARRAY_SIZE(wm8990_dapm_widgets));
+
+	/* set up the WM8990 audio map */
+	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 div2;
+	u32 n;
+	u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+	unsigned int source)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div->div2 = 1;
+		Ndiv = target / source;
+	} else
+		pll_div->div2 = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+		"WM8990 N value outwith recommended range! N = %d\n", Ndiv);
+
+	pll_div->n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div->k = K;
+}
+
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	u16 reg;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct _pll_div pll_div;
+
+	if (freq_in && freq_out) {
+		pll_factors(&pll_div, freq_out * 4, freq_in);
+
+		/* Turn on PLL */
+		reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+		reg |= WM8990_PLL_ENA;
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+		/* sysclk comes from PLL */
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2);
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
+
+		/* set up N , fractional mode and pre-divisor if neccessary */
+		wm8990_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+			(pll_div.div2?WM8990_PRESCALE:0));
+		wm8990_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
+		wm8990_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
+	} else {
+		/* Turn on PLL */
+		reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+		reg &= ~WM8990_PLL_ENA;
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+	}
+	return 0;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int wm8990_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 wm8990_priv *wm8990 = codec->private_data;
+
+	wm8990->sysclk = freq;
+	return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 audio1, audio3;
+
+	audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+	audio3 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_3);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		audio3 &= ~WM8990_AIF_MSTR1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		audio3 |= WM8990_AIF_MSTR1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	audio1 &= ~WM8990_AIF_FMT_MASK;
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		audio1 |= WM8990_AIF_TMF_I2S;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		audio1 |= WM8990_AIF_TMF_RIGHTJ;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		audio1 |= WM8990_AIF_TMF_LEFTJ;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		audio1 |= WM8990_AIF_TMF_DSP;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		audio1 |= WM8990_AIF_TMF_DSP | WM8990_AIF_LRCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+	return 0;
+}
+
+static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+		int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	switch (div_id) {
+	case WM8990_MCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+			~WM8990_MCLK_DIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		break;
+	case WM8990_DACCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+			~WM8990_DAC_CLKDIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		break;
+	case WM8990_ADCCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+			~WM8990_ADC_CLKDIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		break;
+	case WM8990_BCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_1) &
+			~WM8990_BCLK_DIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_1, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8990_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 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+
+	audio1 &= ~WM8990_AIF_WL_MASK;
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		audio1 |= WM8990_AIF_WL_20BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		audio1 |= WM8990_AIF_WL_24BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		audio1 |= WM8990_AIF_WL_32BITS;
+		break;
+	}
+
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+	return 0;
+}
+
+static int wm8990_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 val;
+
+	val  = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+
+	if (mute)
+		wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+	else
+		wm8990_write(codec, WM8990_DAC_CTRL, val);
+
+	return 0;
+}
+
+static int wm8990_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 val;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Enable all output discharge bits */
+			wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+				WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+				WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+				WM8990_DIS_ROUT);
+
+			/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+				     WM8990_BUFDCOPEN | WM8990_POBCTRL |
+				     WM8990_VMIDTOG);
+
+			/* Delay to allow output caps to discharge */
+			msleep(msecs_to_jiffies(300));
+
+			/* Disable VMIDTOG */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+				     WM8990_BUFDCOPEN | WM8990_POBCTRL);
+
+			/* disable all output discharge bits */
+			wm8990_write(codec, WM8990_ANTIPOP1, 0);
+
+			/* Enable outputs */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+
+			msleep(msecs_to_jiffies(50));
+
+			/* Enable VMID at 2x50k */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+
+			msleep(msecs_to_jiffies(100));
+
+			/* Enable VREF */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+			msleep(msecs_to_jiffies(600));
+
+			/* Enable BUFIOEN */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+				     WM8990_BUFDCOPEN | WM8990_POBCTRL |
+				     WM8990_BUFIOEN);
+
+			/* Disable outputs */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+
+			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+		} else {
+			/* ON -> standby */
+
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Enable POBCTRL and SOFT_ST */
+		wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+			WM8990_POBCTRL | WM8990_BUFIOEN);
+
+		/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+		wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+			WM8990_BUFDCOPEN | WM8990_POBCTRL |
+			WM8990_BUFIOEN);
+
+		/* mute DAC */
+		val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL);
+		wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+
+		/* Enable any disabled outputs */
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+		/* Disable VMID */
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+
+		msleep(msecs_to_jiffies(300));
+
+		/* Enable all output discharge bits */
+		wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+			WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+			WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+			WM8990_DIS_ROUT);
+
+		/* Disable VREF */
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+
+		/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+		wm8990_write(codec, WM8990_ANTIPOP2, 0x0);
+		break;
+	}
+
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8990_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 WM8990_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * The WM8990 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+struct snd_soc_dai wm8990_dai = {
+/* ADC/DAC on primary */
+	.name = "WM8990 ADC/DAC Primary",
+	.id = 1,
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8990_RATES,
+		.formats = WM8990_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8990_RATES,
+		.formats = WM8990_FORMATS,},
+	.ops = {
+		.hw_params = wm8990_hw_params,},
+	.dai_ops = {
+		.digital_mute = wm8990_mute,
+		.set_fmt = wm8990_set_dai_fmt,
+		.set_clkdiv = wm8990_set_dai_clkdiv,
+		.set_pll = wm8990_set_dai_pll,
+		.set_sysclk = wm8990_set_dai_sysclk,
+	},
+};
+EXPORT_SYMBOL_GPL(wm8990_dai);
+
+static int wm8990_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;
+
+	/* we only need to suspend if we are a valid card */
+	if (!codec->card)
+		return 0;
+
+	wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8990_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;
+
+	/* we only need to resume if we are a valid card */
+	if (!codec->card)
+		return 0;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
+		if (i + 1 == WM8990_RESET)
+			continue;
+		data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+
+	wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	return 0;
+}
+
+/*
+ * initialise the WM8990 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8990_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 reg;
+	int ret = 0;
+
+	codec->name = "WM8990";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8990_read_reg_cache;
+	codec->write = wm8990_write;
+	codec->set_bias_level = wm8990_set_bias_level;
+	codec->dai = &wm8990_dai;
+	codec->num_dai = 2;
+	codec->reg_cache_size = ARRAY_SIZE(wm8990_reg);
+	codec->reg_cache = kmemdup(wm8990_reg, sizeof(wm8990_reg), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	wm8990_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8990: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* charge output caps */
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	reg = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_4);
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
+
+	reg = wm8990_read_reg_cache(codec, WM8990_GPIO1_GPIO2) &
+		~WM8990_GPIO1_SEL_MASK;
+	wm8990_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
+
+	reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+	wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
+
+	wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+	wm8990_add_controls(codec);
+	wm8990_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8990: 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 *wm8990_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM891 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    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)
+{
+	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) {
+		kfree(codec);
+		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) {
+		pr_err("failed to initialise WM8990\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int wm8990_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 wm8990_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8990_codec_probe);
+}
+
+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,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8990",
+	.driver = &wm8990_i2c_driver,
+};
+#endif
+
+static int wm8990_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8990_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct wm8990_priv *wm8990;
+	int ret = 0;
+
+	pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	wm8990 = kzalloc(sizeof(struct wm8990_priv), GFP_KERNEL);
+	if (wm8990 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = wm8990;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	wm8990_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(&wm8990_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 wm8990_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)
+		wm8990_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(&wm8990_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8990 = {
+	.probe =	wm8990_probe,
+	.remove =	wm8990_remove,
+	.suspend =	wm8990_suspend,
+	.resume =	wm8990_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
+
+MODULE_DESCRIPTION("ASoC WM8990 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
new file mode 100644
index 0000000000000000000000000000000000000000..6bea57485283736eab9002bbac738b3eb7c59879
--- /dev/null
+++ b/sound/soc/codecs/wm8990.h
@@ -0,0 +1,832 @@
+/*
+ * wm8990.h  --  audio driver for WM8990
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __WM8990REGISTERDEFS_H__
+#define __WM8990REGISTERDEFS_H__
+
+/*
+ * Register values.
+ */
+#define WM8990_RESET                            0x00
+#define WM8990_POWER_MANAGEMENT_1               0x01
+#define WM8990_POWER_MANAGEMENT_2               0x02
+#define WM8990_POWER_MANAGEMENT_3               0x03
+#define WM8990_AUDIO_INTERFACE_1                0x04
+#define WM8990_AUDIO_INTERFACE_2                0x05
+#define WM8990_CLOCKING_1                       0x06
+#define WM8990_CLOCKING_2                       0x07
+#define WM8990_AUDIO_INTERFACE_3                0x08
+#define WM8990_AUDIO_INTERFACE_4                0x09
+#define WM8990_DAC_CTRL                         0x0A
+#define WM8990_LEFT_DAC_DIGITAL_VOLUME          0x0B
+#define WM8990_RIGHT_DAC_DIGITAL_VOLUME         0x0C
+#define WM8990_DIGITAL_SIDE_TONE                0x0D
+#define WM8990_ADC_CTRL                         0x0E
+#define WM8990_LEFT_ADC_DIGITAL_VOLUME          0x0F
+#define WM8990_RIGHT_ADC_DIGITAL_VOLUME         0x10
+#define WM8990_GPIO_CTRL_1                      0x12
+#define WM8990_GPIO1_GPIO2                      0x13
+#define WM8990_GPIO3_GPIO4                      0x14
+#define WM8990_GPIO5_GPIO6                      0x15
+#define WM8990_GPIOCTRL_2                       0x16
+#define WM8990_GPIO_POL                         0x17
+#define WM8990_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8990_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8990_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8990_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8990_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8990_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8990_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8990_OUT3_4_VOLUME                    0x1F
+#define WM8990_LEFT_OPGA_VOLUME                 0x20
+#define WM8990_RIGHT_OPGA_VOLUME                0x21
+#define WM8990_SPEAKER_VOLUME                   0x22
+#define WM8990_CLASSD1                          0x23
+#define WM8990_CLASSD3                          0x25
+#define WM8990_INPUT_MIXER1                     0x27
+#define WM8990_INPUT_MIXER2                     0x28
+#define WM8990_INPUT_MIXER3                     0x29
+#define WM8990_INPUT_MIXER4                     0x2A
+#define WM8990_INPUT_MIXER5                     0x2B
+#define WM8990_INPUT_MIXER6                     0x2C
+#define WM8990_OUTPUT_MIXER1                    0x2D
+#define WM8990_OUTPUT_MIXER2                    0x2E
+#define WM8990_OUTPUT_MIXER3                    0x2F
+#define WM8990_OUTPUT_MIXER4                    0x30
+#define WM8990_OUTPUT_MIXER5                    0x31
+#define WM8990_OUTPUT_MIXER6                    0x32
+#define WM8990_OUT3_4_MIXER                     0x33
+#define WM8990_LINE_MIXER1                      0x34
+#define WM8990_LINE_MIXER2                      0x35
+#define WM8990_SPEAKER_MIXER                    0x36
+#define WM8990_ADDITIONAL_CONTROL               0x37
+#define WM8990_ANTIPOP1                         0x38
+#define WM8990_ANTIPOP2                         0x39
+#define WM8990_MICBIAS                          0x3A
+#define WM8990_PLL1                             0x3C
+#define WM8990_PLL2                             0x3D
+#define WM8990_PLL3                             0x3E
+#define WM8990_INTDRIVBITS			0x3F
+
+#define WM8990_REGISTER_COUNT                   60
+#define WM8990_MAX_REGISTER                     0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8990_SW_RESET_CHIP_ID_MASK            0xFFFF  /* SW_RESET_CHIP_ID */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8990_SPK_ENA                          0x1000  /* SPK_ENA */
+#define WM8990_SPK_ENA_BIT			12
+#define WM8990_OUT3_ENA                         0x0800  /* OUT3_ENA */
+#define WM8990_OUT3_ENA_BIT			11
+#define WM8990_OUT4_ENA                         0x0400  /* OUT4_ENA */
+#define WM8990_OUT4_ENA_BIT			10
+#define WM8990_LOUT_ENA                         0x0200  /* LOUT_ENA */
+#define WM8990_LOUT_ENA_BIT			9
+#define WM8990_ROUT_ENA                         0x0100  /* ROUT_ENA */
+#define WM8990_ROUT_ENA_BIT			8
+#define WM8990_MICBIAS_ENA                      0x0010  /* MICBIAS_ENA */
+#define WM8990_MICBIAS_ENA_BIT			4
+#define WM8990_VMID_MODE_MASK                   0x0006  /* VMID_MODE - [2:1] */
+#define WM8990_VREF_ENA                         0x0001  /* VREF_ENA */
+#define WM8990_VREF_ENA_BIT			0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8990_PLL_ENA                          0x8000  /* PLL_ENA */
+#define WM8990_PLL_ENA_BIT			15
+#define WM8990_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8990_TSHUT_ENA_BIT			14
+#define WM8990_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8990_TSHUT_OPDIS_BIT			13
+#define WM8990_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8990_OPCLK_ENA_BIT			11
+#define WM8990_AINL_ENA                         0x0200  /* AINL_ENA */
+#define WM8990_AINL_ENA_BIT			9
+#define WM8990_AINR_ENA                         0x0100  /* AINR_ENA */
+#define WM8990_AINR_ENA_BIT			8
+#define WM8990_LIN34_ENA                        0x0080  /* LIN34_ENA */
+#define WM8990_LIN34_ENA_BIT			7
+#define WM8990_LIN12_ENA                        0x0040  /* LIN12_ENA */
+#define WM8990_LIN12_ENA_BIT			6
+#define WM8990_RIN34_ENA                        0x0020  /* RIN34_ENA */
+#define WM8990_RIN34_ENA_BIT			5
+#define WM8990_RIN12_ENA                        0x0010  /* RIN12_ENA */
+#define WM8990_RIN12_ENA_BIT			4
+#define WM8990_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8990_ADCL_ENA_BIT			1
+#define WM8990_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8990_ADCR_ENA_BIT			0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8990_LON_ENA                          0x2000  /* LON_ENA */
+#define WM8990_LON_ENA_BIT			13
+#define WM8990_LOP_ENA                          0x1000  /* LOP_ENA */
+#define WM8990_LOP_ENA_BIT			12
+#define WM8990_RON_ENA                          0x0800  /* RON_ENA */
+#define WM8990_RON_ENA_BIT			11
+#define WM8990_ROP_ENA                          0x0400  /* ROP_ENA */
+#define WM8990_ROP_ENA_BIT			10
+#define WM8990_LOPGA_ENA                        0x0080  /* LOPGA_ENA */
+#define WM8990_LOPGA_ENA_BIT			7
+#define WM8990_ROPGA_ENA                        0x0040  /* ROPGA_ENA */
+#define WM8990_ROPGA_ENA_BIT			6
+#define WM8990_LOMIX_ENA                        0x0020  /* LOMIX_ENA */
+#define WM8990_LOMIX_ENA_BIT			5
+#define WM8990_ROMIX_ENA                        0x0010  /* ROMIX_ENA */
+#define WM8990_ROMIX_ENA_BIT			4
+#define WM8990_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8990_DACL_ENA_BIT			1
+#define WM8990_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8990_DACR_ENA_BIT			0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8990_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8990_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8990_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8990_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8990_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8990_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8990_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8990_AIF_WL_16BITS			(0 << 5)
+#define WM8990_AIF_WL_20BITS			(1 << 5)
+#define WM8990_AIF_WL_24BITS			(2 << 5)
+#define WM8990_AIF_WL_32BITS			(3 << 5)
+#define WM8990_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8990_AIF_TMF_RIGHTJ			(0 << 3)
+#define WM8990_AIF_TMF_LEFTJ			(1 << 3)
+#define WM8990_AIF_TMF_I2S			(2 << 3)
+#define WM8990_AIF_TMF_DSP			(3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8990_DACL_SRC                         0x8000  /* DACL_SRC */
+#define WM8990_DACR_SRC                         0x4000  /* DACR_SRC */
+#define WM8990_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8990_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8990_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST */
+#define WM8990_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8990_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8990_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8990_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8990_LOOPBACK                         0x0001  /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8990_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8990_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8990_OPCLKDIV_MASK                    0x1E00  /* OPCLKDIV - [12:9] */
+#define WM8990_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8990_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8990_BCLK_DIV_1			(0x0 << 1)
+#define WM8990_BCLK_DIV_1_5			(0x1 << 1)
+#define WM8990_BCLK_DIV_2			(0x2 << 1)
+#define WM8990_BCLK_DIV_3			(0x3 << 1)
+#define WM8990_BCLK_DIV_4			(0x4 << 1)
+#define WM8990_BCLK_DIV_5_5			(0x5 << 1)
+#define WM8990_BCLK_DIV_6			(0x6 << 1)
+#define WM8990_BCLK_DIV_8			(0x7 << 1)
+#define WM8990_BCLK_DIV_11			(0x8 << 1)
+#define WM8990_BCLK_DIV_12			(0x9 << 1)
+#define WM8990_BCLK_DIV_16			(0xA << 1)
+#define WM8990_BCLK_DIV_22			(0xB << 1)
+#define WM8990_BCLK_DIV_24			(0xC << 1)
+#define WM8990_BCLK_DIV_32			(0xD << 1)
+#define WM8990_BCLK_DIV_44			(0xE << 1)
+#define WM8990_BCLK_DIV_48			(0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8990_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8990_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8990_CLK_FORCE                        0x2000  /* CLK_FORCE */
+#define WM8990_MCLK_DIV_MASK                    0x1800  /* MCLK_DIV - [12:11] */
+#define WM8990_MCLK_DIV_1			(0 << 11)
+#define WM8990_MCLK_DIV_2			(2 << 11)
+#define WM8990_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8990_ADC_CLKDIV_MASK                  0x00E0  /* ADC_CLKDIV */
+#define WM8990_ADC_CLKDIV_1			(0 << 5)
+#define WM8990_ADC_CLKDIV_1_5			(1 << 5)
+#define WM8990_ADC_CLKDIV_2			(2 << 5)
+#define WM8990_ADC_CLKDIV_3			(3 << 5)
+#define WM8990_ADC_CLKDIV_4			(4 << 5)
+#define WM8990_ADC_CLKDIV_5_5			(5 << 5)
+#define WM8990_ADC_CLKDIV_6			(6 << 5)
+#define WM8990_DAC_CLKDIV_MASK                  0x001C  /* DAC_CLKDIV - [4:2] */
+#define WM8990_DAC_CLKDIV_1			(0 << 2)
+#define WM8990_DAC_CLKDIV_1_5			(1 << 2)
+#define WM8990_DAC_CLKDIV_2			(2 << 2)
+#define WM8990_DAC_CLKDIV_3			(3 << 2)
+#define WM8990_DAC_CLKDIV_4			(4 << 2)
+#define WM8990_DAC_CLKDIV_5_5			(5 << 2)
+#define WM8990_DAC_CLKDIV_6			(6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8990_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8990_AIF_MSTR2                        0x4000  /* AIF_MSTR2 */
+#define WM8990_AIF_SEL                          0x2000  /* AIF_SEL */
+#define WM8990_ADCLRC_DIR                       0x0800  /* ADCLRC_DIR */
+#define WM8990_ADCLRC_RATE_MASK                 0x07FF  /* ADCLRC_RATE */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8990_ALRCGPIO1                        0x8000  /* ALRCGPIO1 */
+#define WM8990_ALRCBGPIO6                       0x4000  /* ALRCBGPIO6 */
+#define WM8990_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8990_DACLRC_DIR                       0x0800  /* DACLRC_DIR */
+#define WM8990_DACLRC_RATE_MASK                 0x07FF  /* DACLRC_RATE */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8990_AIF_LRCLKRATE                    0x0400  /* AIF_LRCLKRATE */
+#define WM8990_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8990_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8990_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8990_DAC_MUTEMODE                     0x0040  /* DAC_MUTEMODE */
+#define WM8990_DEEMP_MASK                       0x0030  /* DEEMP - [5:4] */
+#define WM8990_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8990_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8990_DACR_DATINV                      0x0001  /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8990_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8990_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8990_DACL_VOL_SHIFT			0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8990_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8990_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8990_DACR_VOL_SHIFT			0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8990_ADCL_DAC_SVOL_MASK               0x0F  /* ADCL_DAC_SVOL */
+#define WM8990_ADCL_DAC_SVOL_SHIFT		9
+#define WM8990_ADCR_DAC_SVOL_MASK               0x0F  /* ADCR_DAC_SVOL */
+#define WM8990_ADCR_DAC_SVOL_SHIFT		5
+#define WM8990_ADC_TO_DACL_MASK                 0x03  /* ADC_TO_DACL - [3:2] */
+#define WM8990_ADC_TO_DACL_SHIFT		2
+#define WM8990_ADC_TO_DACR_MASK                 0x03  /* ADC_TO_DACR - [1:0] */
+#define WM8990_ADC_TO_DACR_SHIFT		0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8990_ADC_HPF_ENA                      0x0100  /* ADC_HPF_ENA */
+#define WM8990_ADC_HPF_ENA_BIT			8
+#define WM8990_ADC_HPF_CUT_MASK                 0x03  /* ADC_HPF_CUT - [6:5] */
+#define WM8990_ADC_HPF_CUT_SHIFT		5
+#define WM8990_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8990_ADCL_DATINV_BIT			1
+#define WM8990_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8990_ADCR_DATINV_BIT			0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8990_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8990_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8990_ADCL_VOL_SHIFT			0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8990_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8990_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8990_ADCR_VOL_SHIFT			0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8990_IRQ                              0x1000  /* IRQ */
+#define WM8990_TEMPOK                           0x0800  /* TEMPOK */
+#define WM8990_MICSHRT                          0x0400  /* MICSHRT */
+#define WM8990_MICDET                           0x0200  /* MICDET */
+#define WM8990_PLL_LCK                          0x0100  /* PLL_LCK */
+#define WM8990_GPI8_STATUS                      0x0080  /* GPI8_STATUS */
+#define WM8990_GPI7_STATUS                      0x0040  /* GPI7_STATUS */
+#define WM8990_GPIO6_STATUS                     0x0020  /* GPIO6_STATUS */
+#define WM8990_GPIO5_STATUS                     0x0010  /* GPIO5_STATUS */
+#define WM8990_GPIO4_STATUS                     0x0008  /* GPIO4_STATUS */
+#define WM8990_GPIO3_STATUS                     0x0004  /* GPIO3_STATUS */
+#define WM8990_GPIO2_STATUS                     0x0002  /* GPIO2_STATUS */
+#define WM8990_GPIO1_STATUS                     0x0001  /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8990_GPIO2_DEB_ENA                    0x8000  /* GPIO2_DEB_ENA */
+#define WM8990_GPIO2_IRQ_ENA                    0x4000  /* GPIO2_IRQ_ENA */
+#define WM8990_GPIO2_PU                         0x2000  /* GPIO2_PU */
+#define WM8990_GPIO2_PD                         0x1000  /* GPIO2_PD */
+#define WM8990_GPIO2_SEL_MASK                   0x0F00  /* GPIO2_SEL - [11:8] */
+#define WM8990_GPIO1_DEB_ENA                    0x0080  /* GPIO1_DEB_ENA */
+#define WM8990_GPIO1_IRQ_ENA                    0x0040  /* GPIO1_IRQ_ENA */
+#define WM8990_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8990_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8990_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8990_GPIO4_DEB_ENA                    0x8000  /* GPIO4_DEB_ENA */
+#define WM8990_GPIO4_IRQ_ENA                    0x4000  /* GPIO4_IRQ_ENA */
+#define WM8990_GPIO4_PU                         0x2000  /* GPIO4_PU */
+#define WM8990_GPIO4_PD                         0x1000  /* GPIO4_PD */
+#define WM8990_GPIO4_SEL_MASK                   0x0F00  /* GPIO4_SEL - [11:8] */
+#define WM8990_GPIO3_DEB_ENA                    0x0080  /* GPIO3_DEB_ENA */
+#define WM8990_GPIO3_IRQ_ENA                    0x0040  /* GPIO3_IRQ_ENA */
+#define WM8990_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8990_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8990_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8990_GPIO6_DEB_ENA                    0x8000  /* GPIO6_DEB_ENA */
+#define WM8990_GPIO6_IRQ_ENA                    0x4000  /* GPIO6_IRQ_ENA */
+#define WM8990_GPIO6_PU                         0x2000  /* GPIO6_PU */
+#define WM8990_GPIO6_PD                         0x1000  /* GPIO6_PD */
+#define WM8990_GPIO6_SEL_MASK                   0x0F00  /* GPIO6_SEL - [11:8] */
+#define WM8990_GPIO5_DEB_ENA                    0x0080  /* GPIO5_DEB_ENA */
+#define WM8990_GPIO5_IRQ_ENA                    0x0040  /* GPIO5_IRQ_ENA */
+#define WM8990_GPIO5_PU                         0x0020  /* GPIO5_PU */
+#define WM8990_GPIO5_PD                         0x0010  /* GPIO5_PD */
+#define WM8990_GPIO5_SEL_MASK                   0x000F  /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8990_RD_3W_ENA                        0x8000  /* RD_3W_ENA */
+#define WM8990_MODE_3W4W                        0x4000  /* MODE_3W4W */
+#define WM8990_TEMPOK_IRQ_ENA                   0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8990_MICSHRT_IRQ_ENA                  0x0400  /* MICSHRT_IRQ_ENA */
+#define WM8990_MICDET_IRQ_ENA                   0x0200  /* MICDET_IRQ_ENA */
+#define WM8990_PLL_LCK_IRQ_ENA                  0x0100  /* PLL_LCK_IRQ_ENA */
+#define WM8990_GPI8_DEB_ENA                     0x0080  /* GPI8_DEB_ENA */
+#define WM8990_GPI8_IRQ_ENA                     0x0040  /* GPI8_IRQ_ENA */
+#define WM8990_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8990_GPI7_DEB_ENA                     0x0008  /* GPI7_DEB_ENA */
+#define WM8990_GPI7_IRQ_ENA                     0x0004  /* GPI7_IRQ_ENA */
+#define WM8990_GPI7_ENA                         0x0001  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8990_IRQ_INV                          0x1000  /* IRQ_INV */
+#define WM8990_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8990_MICSHRT_POL                      0x0400  /* MICSHRT_POL */
+#define WM8990_MICDET_POL                       0x0200  /* MICDET_POL */
+#define WM8990_PLL_LCK_POL                      0x0100  /* PLL_LCK_POL */
+#define WM8990_GPI8_POL                         0x0080  /* GPI8_POL */
+#define WM8990_GPI7_POL                         0x0040  /* GPI7_POL */
+#define WM8990_GPIO6_POL                        0x0020  /* GPIO6_POL */
+#define WM8990_GPIO5_POL                        0x0010  /* GPIO5_POL */
+#define WM8990_GPIO4_POL                        0x0008  /* GPIO4_POL */
+#define WM8990_GPIO3_POL                        0x0004  /* GPIO3_POL */
+#define WM8990_GPIO2_POL                        0x0002  /* GPIO2_POL */
+#define WM8990_GPIO1_POL                        0x0001  /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_LI12MUTE                         0x0080  /* LI12MUTE */
+#define WM8990_LI12MUTE_BIT			7
+#define WM8990_LI12ZC                           0x0040  /* LI12ZC */
+#define WM8990_LI12ZC_BIT			6
+#define WM8990_LIN12VOL_MASK                    0x001F  /* LIN12VOL - [4:0] */
+#define WM8990_LIN12VOL_SHIFT			0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_LI34MUTE                         0x0080  /* LI34MUTE */
+#define WM8990_LI34MUTE_BIT			7
+#define WM8990_LI34ZC                           0x0040  /* LI34ZC */
+#define WM8990_LI34ZC_BIT			6
+#define WM8990_LIN34VOL_MASK                    0x001F  /* LIN34VOL - [4:0] */
+#define WM8990_LIN34VOL_SHIFT			0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_RI12MUTE                         0x0080  /* RI12MUTE */
+#define WM8990_RI12MUTE_BIT			7
+#define WM8990_RI12ZC                           0x0040  /* RI12ZC */
+#define WM8990_RI12ZC_BIT			6
+#define WM8990_RIN12VOL_MASK                    0x001F  /* RIN12VOL - [4:0] */
+#define WM8990_RIN12VOL_SHIFT			0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_RI34MUTE                         0x0080  /* RI34MUTE */
+#define WM8990_RI34MUTE_BIT			7
+#define WM8990_RI34ZC                           0x0040  /* RI34ZC */
+#define WM8990_RI34ZC_BIT			6
+#define WM8990_RIN34VOL_MASK                    0x001F  /* RIN34VOL - [4:0] */
+#define WM8990_RIN34VOL_SHIFT			0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_LOZC                             0x0080  /* LOZC */
+#define WM8990_LOZC_BIT				7
+#define WM8990_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8990_LOUTVOL_SHIFT			0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_ROZC                             0x0080  /* ROZC */
+#define WM8990_ROZC_BIT				7
+#define WM8990_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8990_ROUTVOL_SHIFT			0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8990_LONMUTE                          0x0040  /* LONMUTE */
+#define WM8990_LONMUTE_BIT			6
+#define WM8990_LOPMUTE                          0x0020  /* LOPMUTE */
+#define WM8990_LOPMUTE_BIT			5
+#define WM8990_LOATTN                           0x0010  /* LOATTN */
+#define WM8990_LOATTN_BIT			4
+#define WM8990_RONMUTE                          0x0004  /* RONMUTE */
+#define WM8990_RONMUTE_BIT			2
+#define WM8990_ROPMUTE                          0x0002  /* ROPMUTE */
+#define WM8990_ROPMUTE_BIT			1
+#define WM8990_ROATTN                           0x0001  /* ROATTN */
+#define WM8990_ROATTN_BIT			0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8990_OUT3MUTE                         0x0020  /* OUT3MUTE */
+#define WM8990_OUT3MUTE_BIT			5
+#define WM8990_OUT3ATTN                         0x0010  /* OUT3ATTN */
+#define WM8990_OUT3ATTN_BIT			4
+#define WM8990_OUT4MUTE                         0x0002  /* OUT4MUTE */
+#define WM8990_OUT4MUTE_BIT			1
+#define WM8990_OUT4ATTN                         0x0001  /* OUT4ATTN */
+#define WM8990_OUT4ATTN_BIT			0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_LOPGAZC                          0x0080  /* LOPGAZC */
+#define WM8990_LOPGAZC_BIT			7
+#define WM8990_LOPGAVOL_MASK                    0x007F  /* LOPGAVOL - [6:0] */
+#define WM8990_LOPGAVOL_SHIFT			0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_ROPGAZC                          0x0080  /* ROPGAZC */
+#define WM8990_ROPGAZC_BIT			7
+#define WM8990_ROPGAVOL_MASK                    0x007F  /* ROPGAVOL - [6:0] */
+#define WM8990_ROPGAVOL_SHIFT			0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8990_SPKVOL_MASK                      0x0003  /* SPKVOL - [1:0] */
+#define WM8990_SPKVOL_SHIFT			0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8990_CDMODE                           0x0100  /* CDMODE */
+#define WM8990_CDMODE_BIT			8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8990_DCGAIN_MASK                      0x0007  /* DCGAIN - [5:3] */
+#define WM8990_DCGAIN_SHIFT			3
+#define WM8990_ACGAIN_MASK                      0x0007  /* ACGAIN - [2:0] */
+#define WM8990_ACGAIN_SHIFT			0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8990_AINLMODE_MASK                    0x000C  /* AINLMODE - [3:2] */
+#define WM8990_AINLMODE_SHIFT			2
+#define WM8990_AINRMODE_MASK                    0x0003  /* AINRMODE - [1:0] */
+#define WM8990_AINRMODE_SHIFT			0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8990_LMP4				0x0080	/* LMP4 */
+#define WM8990_LMP4_BIT                         7	/* LMP4 */
+#define WM8990_LMN3                             0x0040  /* LMN3 */
+#define WM8990_LMN3_BIT                         6       /* LMN3 */
+#define WM8990_LMP2                             0x0020  /* LMP2 */
+#define WM8990_LMP2_BIT                         5       /* LMP2 */
+#define WM8990_LMN1                             0x0010  /* LMN1 */
+#define WM8990_LMN1_BIT                         4       /* LMN1 */
+#define WM8990_RMP4                             0x0008  /* RMP4 */
+#define WM8990_RMP4_BIT                         3       /* RMP4 */
+#define WM8990_RMN3                             0x0004  /* RMN3 */
+#define WM8990_RMN3_BIT                         2       /* RMN3 */
+#define WM8990_RMP2                             0x0002  /* RMP2 */
+#define WM8990_RMP2_BIT                         1       /* RMP2 */
+#define WM8990_RMN1                             0x0001  /* RMN1 */
+#define WM8990_RMN1_BIT                         0       /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8990_L34MNB                           0x0100  /* L34MNB */
+#define WM8990_L34MNB_BIT			8
+#define WM8990_L34MNBST                         0x0080  /* L34MNBST */
+#define WM8990_L34MNBST_BIT			7
+#define WM8990_L12MNB                           0x0020  /* L12MNB */
+#define WM8990_L12MNB_BIT			5
+#define WM8990_L12MNBST                         0x0010  /* L12MNBST */
+#define WM8990_L12MNBST_BIT			4
+#define WM8990_LDBVOL_MASK                      0x0007  /* LDBVOL - [2:0] */
+#define WM8990_LDBVOL_SHIFT			0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8990_R34MNB                           0x0100  /* R34MNB */
+#define WM8990_R34MNB_BIT			8
+#define WM8990_R34MNBST                         0x0080  /* R34MNBST */
+#define WM8990_R34MNBST_BIT			7
+#define WM8990_R12MNB                           0x0020  /* R12MNB */
+#define WM8990_R12MNB_BIT			5
+#define WM8990_R12MNBST                         0x0010  /* R12MNBST */
+#define WM8990_R12MNBST_BIT			4
+#define WM8990_RDBVOL_MASK                      0x0007  /* RDBVOL - [2:0] */
+#define WM8990_RDBVOL_SHIFT			0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8990_LI2BVOL_MASK                     0x07  /* LI2BVOL - [8:6] */
+#define WM8990_LI2BVOL_SHIFT			6
+#define WM8990_LR4BVOL_MASK                     0x07  /* LR4BVOL - [5:3] */
+#define WM8990_LR4BVOL_SHIFT			3
+#define WM8990_LL4BVOL_MASK                     0x07  /* LL4BVOL - [2:0] */
+#define WM8990_LL4BVOL_SHIFT			0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8990_RI2BVOL_MASK                     0x07  /* RI2BVOL - [8:6] */
+#define WM8990_RI2BVOL_SHIFT			6
+#define WM8990_RL4BVOL_MASK                     0x07  /* RL4BVOL - [5:3] */
+#define WM8990_RL4BVOL_SHIFT			3
+#define WM8990_RR4BVOL_MASK                     0x07  /* RR4BVOL - [2:0] */
+#define WM8990_RR4BVOL_SHIFT			0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8990_LRBLO                            0x0080  /* LRBLO */
+#define WM8990_LRBLO_BIT			7
+#define WM8990_LLBLO                            0x0040  /* LLBLO */
+#define WM8990_LLBLO_BIT			6
+#define WM8990_LRI3LO                           0x0020  /* LRI3LO */
+#define WM8990_LRI3LO_BIT			5
+#define WM8990_LLI3LO                           0x0010  /* LLI3LO */
+#define WM8990_LLI3LO_BIT			4
+#define WM8990_LR12LO                           0x0008  /* LR12LO */
+#define WM8990_LR12LO_BIT			3
+#define WM8990_LL12LO                           0x0004  /* LL12LO */
+#define WM8990_LL12LO_BIT			2
+#define WM8990_LDLO                             0x0001  /* LDLO */
+#define WM8990_LDLO_BIT				0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8990_RLBRO                            0x0080  /* RLBRO */
+#define WM8990_RLBRO_BIT			7
+#define WM8990_RRBRO                            0x0040  /* RRBRO */
+#define WM8990_RRBRO_BIT			6
+#define WM8990_RLI3RO                           0x0020  /* RLI3RO */
+#define WM8990_RLI3RO_BIT			5
+#define WM8990_RRI3RO                           0x0010  /* RRI3RO */
+#define WM8990_RRI3RO_BIT			4
+#define WM8990_RL12RO                           0x0008  /* RL12RO */
+#define WM8990_RL12RO_BIT			3
+#define WM8990_RR12RO                           0x0004  /* RR12RO */
+#define WM8990_RR12RO_BIT			2
+#define WM8990_RDRO                             0x0001  /* RDRO */
+#define WM8990_RDRO_BIT				0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8990_LLI3LOVOL_MASK                   0x07  /* LLI3LOVOL - [8:6] */
+#define WM8990_LLI3LOVOL_SHIFT			6
+#define WM8990_LR12LOVOL_MASK                   0x07  /* LR12LOVOL - [5:3] */
+#define WM8990_LR12LOVOL_SHIFT			3
+#define WM8990_LL12LOVOL_MASK                   0x07  /* LL12LOVOL - [2:0] */
+#define WM8990_LL12LOVOL_SHIFT			0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8990_RRI3ROVOL_MASK                   0x07  /* RRI3ROVOL - [8:6] */
+#define WM8990_RRI3ROVOL_SHIFT			6
+#define WM8990_RL12ROVOL_MASK                   0x07  /* RL12ROVOL - [5:3] */
+#define WM8990_RL12ROVOL_SHIFT			3
+#define WM8990_RR12ROVOL_MASK                   0x07  /* RR12ROVOL - [2:0] */
+#define WM8990_RR12ROVOL_SHIFT			0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8990_LRI3LOVOL_MASK                   0x07  /* LRI3LOVOL - [8:6] */
+#define WM8990_LRI3LOVOL_SHIFT			6
+#define WM8990_LRBLOVOL_MASK                    0x07  /* LRBLOVOL - [5:3] */
+#define WM8990_LRBLOVOL_SHIFT			3
+#define WM8990_LLBLOVOL_MASK                    0x07  /* LLBLOVOL - [2:0] */
+#define WM8990_LLBLOVOL_SHIFT			0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8990_RLI3ROVOL_MASK                   0x07  /* RLI3ROVOL - [8:6] */
+#define WM8990_RLI3ROVOL_SHIFT			6
+#define WM8990_RLBROVOL_MASK                    0x07  /* RLBROVOL - [5:3] */
+#define WM8990_RLBROVOL_SHIFT			3
+#define WM8990_RRBROVOL_MASK                    0x07  /* RRBROVOL - [2:0] */
+#define WM8990_RRBROVOL_SHIFT			0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8990_VSEL_MASK                        0x0180  /* VSEL - [8:7] */
+#define WM8990_LI4O3                            0x0020  /* LI4O3 */
+#define WM8990_LI4O3_BIT			5
+#define WM8990_LPGAO3                           0x0010  /* LPGAO3 */
+#define WM8990_LPGAO3_BIT			4
+#define WM8990_RI4O4                            0x0002  /* RI4O4 */
+#define WM8990_RI4O4_BIT			1
+#define WM8990_RPGAO4                           0x0001  /* RPGAO4 */
+#define WM8990_RPGAO4_BIT			0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8990_LLOPGALON                        0x0040  /* LLOPGALON */
+#define WM8990_LLOPGALON_BIT			6
+#define WM8990_LROPGALON                        0x0020  /* LROPGALON */
+#define WM8990_LROPGALON_BIT			5
+#define WM8990_LOPLON                           0x0010  /* LOPLON */
+#define WM8990_LOPLON_BIT			4
+#define WM8990_LR12LOP                          0x0004  /* LR12LOP */
+#define WM8990_LR12LOP_BIT			2
+#define WM8990_LL12LOP                          0x0002  /* LL12LOP */
+#define WM8990_LL12LOP_BIT			1
+#define WM8990_LLOPGALOP                        0x0001  /* LLOPGALOP */
+#define WM8990_LLOPGALOP_BIT			0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8990_RROPGARON                        0x0040  /* RROPGARON */
+#define WM8990_RROPGARON_BIT			6
+#define WM8990_RLOPGARON                        0x0020  /* RLOPGARON */
+#define WM8990_RLOPGARON_BIT			5
+#define WM8990_ROPRON                           0x0010  /* ROPRON */
+#define WM8990_ROPRON_BIT			4
+#define WM8990_RL12ROP                          0x0004  /* RL12ROP */
+#define WM8990_RL12ROP_BIT			2
+#define WM8990_RR12ROP                          0x0002  /* RR12ROP */
+#define WM8990_RR12ROP_BIT			1
+#define WM8990_RROPGAROP                        0x0001  /* RROPGAROP */
+#define WM8990_RROPGAROP_BIT			0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8990_LB2SPK                           0x0080  /* LB2SPK */
+#define WM8990_LB2SPK_BIT			7
+#define WM8990_RB2SPK                           0x0040  /* RB2SPK */
+#define WM8990_RB2SPK_BIT			6
+#define WM8990_LI2SPK                           0x0020  /* LI2SPK */
+#define WM8990_LI2SPK_BIT			5
+#define WM8990_RI2SPK                           0x0010  /* RI2SPK */
+#define WM8990_RI2SPK_BIT			4
+#define WM8990_LOPGASPK                         0x0008  /* LOPGASPK */
+#define WM8990_LOPGASPK_BIT			3
+#define WM8990_ROPGASPK                         0x0004  /* ROPGASPK */
+#define WM8990_ROPGASPK_BIT			2
+#define WM8990_LDSPK                            0x0002  /* LDSPK */
+#define WM8990_LDSPK_BIT			1
+#define WM8990_RDSPK                            0x0001  /* RDSPK */
+#define WM8990_RDSPK_BIT			0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8990_VROI                             0x0001  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8990_DIS_LLINE                        0x0020  /* DIS_LLINE */
+#define WM8990_DIS_RLINE                        0x0010  /* DIS_RLINE */
+#define WM8990_DIS_OUT3                         0x0008  /* DIS_OUT3 */
+#define WM8990_DIS_OUT4                         0x0004  /* DIS_OUT4 */
+#define WM8990_DIS_LOUT                         0x0002  /* DIS_LOUT */
+#define WM8990_DIS_ROUT                         0x0001  /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8990_SOFTST                           0x0040  /* SOFTST */
+#define WM8990_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8990_BUFDCOPEN                        0x0004  /* BUFDCOPEN */
+#define WM8990_POBCTRL                          0x0002  /* POBCTRL */
+#define WM8990_VMIDTOG                          0x0001  /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8990_MCDSCTH_MASK                     0x00C0  /* MCDSCTH - [7:6] */
+#define WM8990_MCDTHR_MASK                      0x0038  /* MCDTHR - [5:3] */
+#define WM8990_MCD                              0x0004  /* MCD */
+#define WM8990_MBSEL                            0x0001  /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8990_SDM                              0x0080  /* SDM */
+#define WM8990_PRESCALE                         0x0040  /* PRESCALE */
+#define WM8990_PLLN_MASK                        0x000F  /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8990_PLLK1_MASK                       0x00FF  /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8990_PLLK2_MASK                       0x00FF  /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8990_INMIXL_PWR_BIT			0
+#define WM8990_AINLMUX_PWR_BIT			1
+#define WM8990_INMIXR_PWR_BIT			2
+#define WM8990_AINRMUX_PWR_BIT			3
+
+struct wm8990_setup_data {
+	unsigned short i2c_address;
+};
+
+#define WM8990_MCLK_DIV 0
+#define WM8990_DACCLK_DIV 1
+#define WM8990_ADCCLK_DIV 2
+#define WM8990_BCLK_DIV 3
+
+extern struct snd_soc_dai wm8990_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8990;
+
+#endif	/* __WM8990REGISTERDEFS_H__ */
+/*------------------------------ END OF FILE ---------------------------------*/
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 76c1e2d33e7d0b8d0b5d22a6dd7dc5fbf7fa37f6..9fc8edd82225cfe98dfbcb6aabadddf48eb1e9c5 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -9,9 +9,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    4th Feb 2006   Initial version.
  */
 
 #include <linux/init.h>
@@ -25,6 +22,7 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include "wm9712.h"
 
 #define WM9712_VERSION "0.4"
 
@@ -351,7 +349,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
 SND_SOC_DAPM_INPUT("MIC2"),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* virtual mixer - mixes left & right channels for spk and mono */
 	{"AC97 Mixer", NULL, "Left DAC"},
 	{"AC97 Mixer", NULL, "Right DAC"},
@@ -446,21 +444,14 @@ static const char *audio_map[][3] = {
 	{"Speaker PGA", NULL, "Speaker Mux"},
 	{"LOUT2", NULL, "Speaker PGA"},
 	{"ROUT2", NULL, "Speaker PGA"},
-
-	{NULL, NULL, NULL},
 };
 
 static int wm9712_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm9712_dapm_widgets,
+				  ARRAY_SIZE(wm9712_dapm_widgets));
 
-	/* set up audio path connects */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-					   audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -541,7 +532,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
 		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
 		SNDRV_PCM_RATE_48000)
 
-struct snd_soc_codec_dai wm9712_dai[] = {
+struct snd_soc_dai wm9712_dai[] = {
 {
 	.name = "AC97 HiFi",
 	.type = SND_SOC_DAI_AC97_BUS,
@@ -574,23 +565,23 @@ struct snd_soc_codec_dai wm9712_dai[] = {
 };
 EXPORT_SYMBOL_GPL(wm9712_dai);
 
-static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9712_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		ac97_write(codec, AC97_POWERDOWN, 0x0000);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		/* disable everything including AC link */
 		ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
 		ac97_write(codec, AC97_POWERDOWN, 0xffff);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -598,12 +589,12 @@ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
 {
 	if (try_warm && soc_ac97_ops.warm_reset) {
 		soc_ac97_ops.warm_reset(codec->ac97);
-		if (!(ac97_read(codec, 0) & 0x8000))
+		if (ac97_read(codec, 0) == wm9712_reg[0])
 			return 1;
 	}
 
 	soc_ac97_ops.reset(codec->ac97);
-	if (ac97_read(codec, 0) & 0x8000)
+	if (ac97_read(codec, 0) != wm9712_reg[0])
 		goto err;
 	return 0;
 
@@ -618,7 +609,7 @@ static int wm9712_soc_suspend(struct platform_device *pdev,
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->codec;
 
-	wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -635,7 +626,7 @@ static int wm9712_soc_resume(struct platform_device *pdev)
 		return ret;
 	}
 
-	wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	if (ret == 0) {
 		/* Sync reg_cache with the hardware after cold reset */
@@ -647,8 +638,8 @@ static int wm9712_soc_resume(struct platform_device *pdev)
 		}
 	}
 
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
-		wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+		wm9712_set_bias_level(codec, SND_SOC_BIAS_ON);
 
 	return ret;
 }
@@ -682,7 +673,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
 	codec->num_dai = ARRAY_SIZE(wm9712_dai);
 	codec->write = ac97_write;
 	codec->read = ac97_read;
-	codec->dapm_event = wm9712_dapm_event;
+	codec->set_bias_level = wm9712_set_bias_level;
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
@@ -706,7 +697,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
 	/* set alc mux to none */
 	ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
 
-	wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm9712_add_controls(codec);
 	wm9712_add_widgets(codec);
 	ret = snd_soc_register_card(socdev);
diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h
index 719105d61e6504c1083e7fdf920251ba075b4575..d29e8a18ca6d2abc6aa67d1c717f571978703a82 100644
--- a/sound/soc/codecs/wm9712.h
+++ b/sound/soc/codecs/wm9712.h
@@ -8,7 +8,7 @@
 #define WM9712_DAI_AC97_HIFI	0
 #define WM9712_DAI_AC97_AUX		1
 
-extern struct snd_soc_codec_dai wm9712_dai[2];
+extern struct snd_soc_dai wm9712_dai[2];
 extern struct snd_soc_codec_device soc_codec_dev_wm9712;
 
 #endif
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 1f241161445ce423135c62adcc9e325d14d4bd05..38d1fe0971fc7faf328c6a19ca0f6f6516f42577 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -10,9 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    4th Feb 2006   Initial version.
- *
  *  Features:-
  *
  *   o Support for AC97 Codec, Voice DAC and Aux DAC
@@ -456,7 +453,7 @@ SND_SOC_DAPM_INPUT("MIC2B"),
 SND_SOC_DAPM_VMID("VMID"),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* left HP mixer */
 	{"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
 	{"Left HP Mixer", "Voice Playback Switch",   "Voice DAC"},
@@ -607,21 +604,14 @@ static const char *audio_map[][3] = {
 	{"Capture Mono Mux", "Stereo", "Capture Mixer"},
 	{"Capture Mono Mux", "Left", "Left Capture Source"},
 	{"Capture Mono Mux", "Right", "Right Capture Source"},
-
-	{NULL, NULL, NULL},
 };
 
 static int wm9713_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm9713_dapm_widgets,
+				  ARRAY_SIZE(wm9713_dapm_widgets));
 
-	/* set up audio path audio_mapnects */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -799,7 +789,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
 	return 0;
 }
 
-static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
 		int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -810,7 +800,7 @@ static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
  * Tristate the PCM DAI lines, tristate can be disabled by calling
  * wm9713_set_dai_fmt()
  */
-static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
 	int tristate)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -826,7 +816,7 @@ static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
  * Configure WM9713 clock dividers.
  * Voice DAC needs 256 FS
  */
-static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 		int div_id, int div)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -868,7 +858,7 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
 	return 0;
 }
 
-static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -886,7 +876,7 @@ static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
 		gpio |= 0x0018;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFS:
-		reg |= 0x0200;
+		reg |= 0x2000;
 		gpio |= 0x001a;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFM:
@@ -1011,15 +1001,24 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
 	return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
 }
 
-#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
-		SNDRV_PCM_RATE_48000)
+#define WM9713_RATES (SNDRV_PCM_RATE_8000  |	\
+		      SNDRV_PCM_RATE_11025 |	\
+		      SNDRV_PCM_RATE_22050 |	\
+		      SNDRV_PCM_RATE_44100 |	\
+		      SNDRV_PCM_RATE_48000)
+
+#define WM9713_PCM_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 WM9713_PCM_FORMATS \
 	(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
 	 SNDRV_PCM_FORMAT_S24_LE)
 
-struct snd_soc_codec_dai wm9713_dai[] = {
+struct snd_soc_dai wm9713_dai[] = {
 {
 	.name = "AC97 HiFi",
 	.type = SND_SOC_DAI_AC97_BUS,
@@ -1061,13 +1060,13 @@ struct snd_soc_codec_dai wm9713_dai[] = {
 		.stream_name = "Voice Playback",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = WM9713_RATES,
+		.rates = WM9713_PCM_RATES,
 		.formats = WM9713_PCM_FORMATS,},
 	.capture = {
 		.stream_name = "Voice Capture",
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = WM9713_RATES,
+		.rates = WM9713_PCM_RATES,
 		.formats = WM9713_PCM_FORMATS,},
 	.ops = {
 		.hw_params = wm9713_pcm_hw_params,
@@ -1086,44 +1085,44 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
 {
 	if (try_warm && soc_ac97_ops.warm_reset) {
 		soc_ac97_ops.warm_reset(codec->ac97);
-		if (!(ac97_read(codec, 0) & 0x8000))
+		if (ac97_read(codec, 0) == wm9713_reg[0])
 			return 1;
 	}
 
 	soc_ac97_ops.reset(codec->ac97);
-	if (ac97_read(codec, 0) & 0x8000)
+	if (ac97_read(codec, 0) != wm9713_reg[0])
 		return -EIO;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(wm9713_reset);
 
-static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9713_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 reg;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* enable thermal shutdown */
 		reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
 		ac97_write(codec, AC97_EXTENDED_MID, reg);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* enable master bias and vmid */
 		reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
 		ac97_write(codec, AC97_EXTENDED_MID, reg);
 		ac97_write(codec, AC97_POWERDOWN, 0x0000);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		/* disable everything including AC link */
 		ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
 		ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
 		ac97_write(codec, AC97_POWERDOWN, 0xffff);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -1160,7 +1159,7 @@ static int wm9713_soc_resume(struct platform_device *pdev)
 		return ret;
 	}
 
-	wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* do we need to re-start the PLL ? */
 	if (wm9713->pll_out)
@@ -1176,8 +1175,8 @@ static int wm9713_soc_resume(struct platform_device *pdev)
 		}
 	}
 
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
-		wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+		wm9713_set_bias_level(codec, SND_SOC_BIAS_ON);
 
 	return ret;
 }
@@ -1216,7 +1215,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
 	codec->num_dai = ARRAY_SIZE(wm9713_dai);
 	codec->write = ac97_write;
 	codec->read = ac97_read;
-	codec->dapm_event = wm9713_dapm_event;
+	codec->set_bias_level = wm9713_set_bias_level;
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
@@ -1238,7 +1237,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
 		goto reset_err;
 	}
 
-	wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* unmute the adc - move to kcontrol */
 	reg = ac97_read(codec, AC97_CD) & 0x7fff;
diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h
index d357b6c8134b7833f13113cd64107fbc8888e85f..63b8d81756e3a50daaeffd777da56539a6f2ad9f 100644
--- a/sound/soc/codecs/wm9713.h
+++ b/sound/soc/codecs/wm9713.h
@@ -46,7 +46,7 @@
 #define WM9713_DAI_PCM_VOICE	2
 
 extern struct snd_soc_codec_device soc_codec_dev_wm9713;
-extern struct snd_soc_codec_dai wm9713_dai[3];
+extern struct snd_soc_dai wm9713_dai[3];
 
 int wm9713_reset(struct snd_soc_codec *codec,  int try_warm);
 
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 20680c551aabc8047fcaf6cd5210dc8914d1736b..8f7e3383490246e81c66c4dc7b92849d768b804e 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -1,6 +1,6 @@
 config SND_DAVINCI_SOC
 	tristate "SoC Audio for the TI DAVINCI chip"
-	depends on ARCH_DAVINCI && SND_SOC
+	depends on ARCH_DAVINCI
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the DAVINCI AC97 or I2S interface. You will also need
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index fcd165240333ff3e0880164a71657cbe4d54e5b3..5e2c306399edac83170615b47bab77e379113036 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -33,24 +33,24 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
 			 struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int ret = 0;
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 					 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_CBM_CFM |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
 				       SND_SOC_DAIFMT_IB_NF);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
 					    SND_SOC_CLOCK_OUT);
 	if (ret < 0)
 		return ret;
@@ -71,7 +71,7 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
 };
 
 /* davinci-evm machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* Headphone connected to HPLOUT, HPROUT */
 	{"Headphone Jack", NULL, "HPLOUT"},
 	{"Headphone Jack", NULL, "HPROUT"},
@@ -90,36 +90,30 @@ static const char *audio_map[][3] = {
 	{"LINE2L", NULL, "Line In"},
 	{"LINE1R", NULL, "Line In"},
 	{"LINE2R", NULL, "Line In"},
-
-	{NULL, NULL, NULL},
 };
 
 /* Logic for a aic3x as connected on a davinci-evm */
 static int evm_aic3x_init(struct snd_soc_codec *codec)
 {
-	int i;
-
 	/* Add davinci-evm specific widgets */
-	for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+				  ARRAY_SIZE(aic3x_dapm_widgets));
 
 	/* Set up davinci-evm specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-					   audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	/* not connected */
-	snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+	snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
+	snd_soc_dapm_disable_pin(codec, "HPLCOM");
+	snd_soc_dapm_disable_pin(codec, "HPRCOM");
 
 	/* always connected */
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
-	snd_soc_dapm_set_endpoint(codec, "Line Out", 1);
-	snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
-	snd_soc_dapm_set_endpoint(codec, "Line In", 1);
+	snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+	snd_soc_dapm_enable_pin(codec, "Line Out");
+	snd_soc_dapm_enable_pin(codec, "Mic Jack");
+	snd_soc_dapm_enable_pin(codec, "Line In");
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index c421774b33ee9b58d9705ada66e546bd4ba9fce9..5ebf1ff71c4cbce6451ecd63dbe080586ac531ea 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -147,7 +147,7 @@ static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
 static int davinci_i2s_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
 
 	cpu_dai->dma_data = dev->dma_params[substream->stream];
@@ -155,7 +155,7 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static int davinci_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 				   unsigned int fmt)
 {
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
@@ -295,11 +295,12 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
 	return ret;
 }
 
-static int davinci_i2s_probe(struct platform_device *pdev)
+static int davinci_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_machine *machine = socdev->machine;
-	struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+	struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
 	struct davinci_mcbsp_dev *dev;
 	struct resource *mem, *ioarea;
 	struct evm_snd_platform_data *pdata;
@@ -356,11 +357,12 @@ err_release_region:
 	return ret;
 }
 
-static void davinci_i2s_remove(struct platform_device *pdev)
+static void davinci_i2s_remove(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_machine *machine = socdev->machine;
-	struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+	struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	struct resource *mem;
 
@@ -376,7 +378,7 @@ static void davinci_i2s_remove(struct platform_device *pdev)
 
 #define DAVINCI_I2S_RATES	SNDRV_PCM_RATE_8000_96000
 
-struct snd_soc_cpu_dai davinci_i2s_dai = {
+struct snd_soc_dai davinci_i2s_dai = {
 	.name = "davinci-i2s",
 	.id = 0,
 	.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
index 9592d17db320c88524901eebcf6da5ef9bb518fc..c5b091807eecf891bb71baa1a208933da3c62a17 100644
--- a/sound/soc/davinci/davinci-i2s.h
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -12,6 +12,6 @@
 #ifndef _DAVINCI_I2S_H
 #define _DAVINCI_I2S_H
 
-extern struct snd_soc_cpu_dai davinci_i2s_dai;
+extern struct snd_soc_dai davinci_i2s_dai;
 
 #endif
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 6a76927c997144c4d6dcbd1e0e317c7d39c82c62..6a5e56a782bb2f9a13551aa3365a6b2b7db85e4a 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -350,7 +350,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
 static u64 davinci_pcm_dmamask = 0xffffffff;
 
 static int davinci_pcm_new(struct snd_card *card,
-			   struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+			   struct snd_soc_dai *dai, struct snd_pcm *pcm)
 {
 	int ret;
 
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 257101f44e9e168de81dcbd2429b740b8c699954..3368ace60977eac389f1b57254e3fcb6c832ab4d 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,8 +1,6 @@
-menu "ALSA SoC audio for Freescale SOCs"
-
 config SND_SOC_MPC8610
 	bool "ALSA SoC support for the MPC8610 SOC"
-	depends on SND_SOC && MPC8610_HPCD
+	depends on MPC8610_HPCD
 	default y if MPC8610
 	help
 	  Say Y if you want to add support for codecs attached to the SSI
@@ -16,5 +14,3 @@ config SND_SOC_MPC8610_HPCD
 	default y if MPC8610_HPCD
 	help
 	  Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
-
-endmenu
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 78de7168d2ba506c767c397f6362ae0c248ef190..da2bc590286438fe7b000cea43e24baf4b6bb417 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -282,7 +282,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
  * once for each .dai_link in the machine driver's snd_soc_machine
  * structure.
  */
-static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
 	struct snd_pcm *pcm)
 {
 	static u64 fsl_dma_dmamask = DMA_BIT_MASK(32);
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h
index 430a6ce8b0d0964f1c2591b07463e4c2e6d6b255..385d4a42603cf3b681ab12c6f050ccd3e11f7bb0 100644
--- a/sound/soc/fsl/fsl_dma.h
+++ b/sound/soc/fsl/fsl_dma.h
@@ -126,7 +126,7 @@ struct fsl_dma_link_descriptor {
 	u8 res[4];      /* Reserved */
 } __attribute__ ((aligned(32), packed));
 
-/* DMA information needed to create a snd_soc_cpu_dai object
+/* DMA information needed to create a snd_soc_dai object
  *
  * ssi_stx_phys: bus address of SSI STX register to use
  * ssi_srx_phys: bus address of SSI SRX register to use
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index f588545698f3687f75941977b8e597791c964e9e..71bff33f5528fe5e34849c06bbc1e88b91fbbcbb 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -82,7 +82,7 @@ struct fsl_ssi_private {
 	struct device *dev;
 	unsigned int playback;
 	unsigned int capture;
-	struct snd_soc_cpu_dai cpu_dai;
+	struct snd_soc_dai cpu_dai;
 	struct device_attribute dev_attr;
 
 	struct {
@@ -479,7 +479,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
  * @freq: the frequency of the given clock ID, currently ignored
  * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
  */
-static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int fsl_ssi_set_sysclk(struct snd_soc_dai *cpu_dai,
 			      int clk_id, unsigned int freq, int dir)
 {
 
@@ -497,7 +497,7 @@ static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
  *
  * @format: one of SND_SOC_DAIFMT_xxx
  */
-static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
+static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
 {
 	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
 }
@@ -505,7 +505,7 @@ static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
 /**
  * fsl_ssi_dai_template: template CPU DAI for the SSI
  */
-static struct snd_soc_cpu_dai fsl_ssi_dai_template = {
+static struct snd_soc_dai fsl_ssi_dai_template = {
 	.playback = {
 		/* The SSI does not support monaural audio. */
 		.channels_min = 2,
@@ -569,15 +569,15 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev,
 }
 
 /**
- * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure
+ * fsl_ssi_create_dai: create a snd_soc_dai structure
  *
- * This function is called by the machine driver to create a snd_soc_cpu_dai
+ * This function is called by the machine driver to create a snd_soc_dai
  * structure.  The function creates an ssi_private object, which contains
- * the snd_soc_cpu_dai.  It also creates the sysfs statistics device.
+ * the snd_soc_dai.  It also creates the sysfs statistics device.
  */
-struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
+struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
 {
-	struct snd_soc_cpu_dai *fsl_ssi_dai;
+	struct snd_soc_dai *fsl_ssi_dai;
 	struct fsl_ssi_private *ssi_private;
 	int ret = 0;
 	struct device_attribute *dev_attr;
@@ -588,7 +588,7 @@ struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
 		return NULL;
 	}
 	memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
-	       sizeof(struct snd_soc_cpu_dai));
+	       sizeof(struct snd_soc_dai));
 
 	fsl_ssi_dai = &ssi_private->cpu_dai;
 	dev_attr = &ssi_private->dev_attr;
@@ -623,11 +623,11 @@ struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
 EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
 
 /**
- * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object
+ * fsl_ssi_destroy_dai: destroy the snd_soc_dai object
  *
  * This function undoes the operations of fsl_ssi_create_dai()
  */
-void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai)
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
 {
 	struct fsl_ssi_private *ssi_private =
 	container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index c5ce88e1565190ae5acd6322c71f665c4850d761..83b44d700e3328fbf973d25c616751555211f202 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -217,8 +217,8 @@ struct fsl_ssi_info {
 	struct device *dev;
 };
 
-struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
-void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai);
+struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai);
 
 #endif
 
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index a00aac7a71f1c10e88b681e4867647bf96d9c328..4bdc9d8fc90e0d8fa3f4f7d6d5f14813c4abe5da 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -58,9 +58,9 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
 		sound_device->dev.platform_data;
 
 	/* Program the signal routing between the SSI and the DMA */
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI);
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI);
 
 	guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
@@ -96,62 +96,52 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
 static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct mpc8610_hpcd_data *machine_data =
 		rtd->socdev->dev->platform_data;
 	int ret = 0;
 
 	/* Tell the CPU driver what the serial protocol is. */
-	if (cpu_dai->dai_ops.set_fmt) {
-		ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
-			machine_data->dai_format);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set CPU driver audio format\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set CPU driver audio format\n");
+		return ret;
 	}
 
 	/* Tell the codec driver what the serial protocol is. */
-	if (codec_dai->dai_ops.set_fmt) {
-		ret = codec_dai->dai_ops.set_fmt(codec_dai,
-			machine_data->dai_format);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set codec driver audio format\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set codec driver audio format\n");
+		return ret;
 	}
 
 	/*
 	 * Tell the CPU driver what the clock frequency is, and whether it's a
 	 * slave or master.
 	 */
-	if (cpu_dai->dai_ops.set_sysclk) {
-		ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, 0,
-			machine_data->clk_frequency,
-			machine_data->cpu_clk_direction);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set CPU driver clock parameters\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_sysclk(cpu_dai, 0,
+					machine_data->clk_frequency,
+					machine_data->cpu_clk_direction);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set CPU driver clock parameters\n");
+		return ret;
 	}
 
 	/*
 	 * Tell the codec driver what the MCLK frequency is, and whether it's
 	 * a slave or master.
 	 */
-	if (codec_dai->dai_ops.set_sysclk) {
-		ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0,
-			machine_data->clk_frequency,
-			machine_data->codec_clk_direction);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set codec driver clock params\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+					machine_data->clk_frequency,
+					machine_data->codec_clk_direction);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set codec driver clock params\n");
+		return ret;
 	}
 
 	return 0;
@@ -170,9 +160,9 @@ int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
 
 	/* Restore the signal routing */
 
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[0], 0);
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[1], 0);
 
 	switch (machine_data->ssi_id) {
@@ -182,7 +172,7 @@ int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
 		break;
 	case 1:
 		clrsetbits_be32(&machine_data->guts->pmuxcr,
-			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
+			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
 		break;
 	}
 
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 0230d83e8e5ef83739f1f4ae4c602e2cffc392ae..aea27e70043cf8f687884227c117ceed224c98f4 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -1,5 +1,3 @@
-menu "SoC Audio for the Texas Instruments OMAP"
-
 config SND_OMAP_SOC
 	tristate "SoC Audio for the Texas Instruments OMAP chips"
 	depends on ARCH_OMAP && SND_SOC
@@ -15,5 +13,3 @@ config SND_OMAP_SOC_N810
 	select SND_SOC_TLV320AIC3X
 	help
 	  Say Y if you want to add support for SoC audio on Nokia N810.
-
-endmenu
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 6533563a6011d01ccb71084c136f79d7a1b808d6..02cec96859b8444133a1cf3a464533bdd87c67cf 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -30,15 +30,15 @@
 
 #include <asm/mach-types.h>
 #include <asm/arch/hardware.h>
-#include <asm/arch/gpio.h>
+#include <linux/gpio.h>
 #include <asm/arch/mcbsp.h>
 
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
 #include "../codecs/tlv320aic3x.h"
 
-#define RX44_HEADSET_AMP_GPIO	10
-#define RX44_SPEAKER_AMP_GPIO	101
+#define N810_HEADSET_AMP_GPIO	10
+#define N810_SPEAKER_AMP_GPIO	101
 
 static struct clk *sys_clkout2;
 static struct clk *sys_clkout2_src;
@@ -46,13 +46,26 @@ static struct clk *func96m_clk;
 
 static int n810_spk_func;
 static int n810_jack_func;
+static int n810_dmic_func;
 
 static void n810_ext_control(struct snd_soc_codec *codec)
 {
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", n810_spk_func);
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", n810_jack_func);
+	if (n810_spk_func)
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	else
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
+
+	if (n810_jack_func)
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+	else
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
 
-	snd_soc_dapm_sync_endpoints(codec);
+	if (n810_dmic_func)
+		snd_soc_dapm_enable_pin(codec, "DMic");
+	else
+		snd_soc_dapm_disable_pin(codec, "DMic");
+
+	snd_soc_dapm_sync(codec);
 }
 
 static int n810_startup(struct snd_pcm_substream *substream)
@@ -73,12 +86,12 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int err;
 
 	/* Set codec DAI configuration */
-	err = codec_dai->dai_ops.set_fmt(codec_dai,
+	err = snd_soc_dai_set_fmt(codec_dai,
 					 SND_SOC_DAIFMT_I2S |
 					 SND_SOC_DAIFMT_NB_NF |
 					 SND_SOC_DAIFMT_CBM_CFM);
@@ -86,7 +99,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
 		return err;
 
 	/* Set cpu DAI configuration */
-	err = cpu_dai->dai_ops.set_fmt(cpu_dai,
+	err = snd_soc_dai_set_fmt(cpu_dai,
 				       SND_SOC_DAIFMT_I2S |
 				       SND_SOC_DAIFMT_NB_NF |
 				       SND_SOC_DAIFMT_CBM_CFM);
@@ -94,7 +107,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
 		return err;
 
 	/* Set the codec system clock for DAC and ADC */
-	err = codec_dai->dai_ops.set_sysclk(codec_dai, 0, 12000000,
+	err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
 					    SND_SOC_CLOCK_IN);
 
 	return err;
@@ -150,13 +163,35 @@ static int n810_set_jack(struct snd_kcontrol *kcontrol,
 	return 1;
 }
 
+static int n810_get_input(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = n810_dmic_func;
+
+	return 0;
+}
+
+static int n810_set_input(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+	if (n810_dmic_func == ucontrol->value.integer.value[0])
+		return 0;
+
+	n810_dmic_func = ucontrol->value.integer.value[0];
+	n810_ext_control(codec);
+
+	return 1;
+}
+
 static int n810_spk_event(struct snd_soc_dapm_widget *w,
 			  struct snd_kcontrol *k, int event)
 {
 	if (SND_SOC_DAPM_EVENT_ON(event))
-		omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 1);
+		gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
 	else
-		omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 0);
+		gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
 
 	return 0;
 }
@@ -165,9 +200,9 @@ static int n810_jack_event(struct snd_soc_dapm_widget *w,
 			   struct snd_kcontrol *k, int event)
 {
 	if (SND_SOC_DAPM_EVENT_ON(event))
-		omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 1);
+		gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
 	else
-		omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 0);
+		gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
 
 	return 0;
 }
@@ -175,21 +210,27 @@ static int n810_jack_event(struct snd_soc_dapm_widget *w,
 static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
 	SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
 	SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
+	SND_SOC_DAPM_MIC("DMic", NULL),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	{"Headphone Jack", NULL, "HPLOUT"},
 	{"Headphone Jack", NULL, "HPROUT"},
 
 	{"Ext Spk", NULL, "LLOUT"},
 	{"Ext Spk", NULL, "RLOUT"},
+
+	{"DMic Rate 64", NULL, "Mic Bias 2V"},
+	{"Mic Bias 2V", NULL, "DMic"},
 };
 
 static const char *spk_function[] = {"Off", "On"};
 static const char *jack_function[] = {"Off", "Headphone"};
+static const char *input_function[] = {"ADC", "Digital Mic"};
 static const struct soc_enum n810_enum[] = {
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
 };
 
 static const struct snd_kcontrol_new aic33_n810_controls[] = {
@@ -197,6 +238,8 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = {
 		     n810_get_spk, n810_set_spk),
 	SOC_ENUM_EXT("Jack Function", n810_enum[1],
 		     n810_get_jack, n810_set_jack),
+	SOC_ENUM_EXT("Input Select",  n810_enum[2],
+		     n810_get_input, n810_set_input),
 };
 
 static int n810_aic33_init(struct snd_soc_codec *codec)
@@ -204,9 +247,9 @@ static int n810_aic33_init(struct snd_soc_codec *codec)
 	int i, err;
 
 	/* Not connected */
-	snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+	snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
+	snd_soc_dapm_disable_pin(codec, "HPLCOM");
+	snd_soc_dapm_disable_pin(codec, "HPRCOM");
 
 	/* Add N810 specific controls */
 	for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
@@ -217,15 +260,13 @@ static int n810_aic33_init(struct snd_soc_codec *codec)
 	}
 
 	/* Add N810 specific widgets */
-	for (i = 0; i < ARRAY_SIZE(aic33_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &aic33_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, aic33_dapm_widgets,
+				  ARRAY_SIZE(aic33_dapm_widgets));
 
 	/* Set up N810 specific audio path audio_map */
-	for (i = 0; i < ARRAY_SIZE(audio_map); i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
@@ -250,6 +291,8 @@ static struct snd_soc_machine snd_soc_machine_n810 = {
 /* Audio private data */
 static struct aic3x_setup_data n810_aic33_setup = {
 	.i2c_address = 0x18,
+	.gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
+	.gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
 };
 
 /* Audio subsystem */
@@ -267,7 +310,7 @@ static int __init n810_soc_init(void)
 	int err;
 	struct device *dev;
 
-	if (!machine_is_nokia_n810())
+	if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
 		return -ENODEV;
 
 	n810_snd_device = platform_device_alloc("soc-audio", -1);
@@ -305,12 +348,12 @@ static int __init n810_soc_init(void)
 	clk_set_parent(sys_clkout2_src, func96m_clk);
 	clk_set_rate(sys_clkout2, 12000000);
 
-	if (omap_request_gpio(RX44_HEADSET_AMP_GPIO) < 0)
+	if (gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0)
 		BUG();
-	if (omap_request_gpio(RX44_SPEAKER_AMP_GPIO) < 0)
+	if (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0)
 		BUG();
-	omap_set_gpio_direction(RX44_HEADSET_AMP_GPIO, 0);
-	omap_set_gpio_direction(RX44_SPEAKER_AMP_GPIO, 0);
+	gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
+	gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
 
 	return 0;
 err2:
@@ -325,6 +368,9 @@ err1:
 
 static void __exit n810_soc_exit(void)
 {
+	gpio_free(N810_SPEAKER_AMP_GPIO);
+	gpio_free(N810_HEADSET_AMP_GPIO);
+
 	platform_device_unregister(n810_snd_device);
 }
 
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 40d87e6d0de8672f7463328de6b112805d36eab5..00b0c9d73cd4ee7460f1260c32efae8d31e5bf18 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -103,7 +103,7 @@ static const unsigned long omap2420_mcbsp_port[][2] = {};
 static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 	int err = 0;
 
@@ -116,7 +116,7 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
 static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 
 	if (!cpu_dai->active) {
@@ -128,7 +128,7 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
 static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 	int err = 0;
 
@@ -157,7 +157,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
 				    struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 	struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
 	int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
@@ -223,7 +223,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
  * This must be called before _set_clkdiv and _set_sysclk since McBSP register
  * cache is initialized here
  */
-static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 				      unsigned int fmt)
 {
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -292,7 +292,7 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
 	return 0;
 }
 
-static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
 				     int div_id, int div)
 {
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -347,7 +347,7 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
 	return 0;
 }
 
-static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 					 int clk_id, unsigned int freq,
 					 int dir)
 {
@@ -376,7 +376,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
 	return err;
 }
 
-struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS] = {
+struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS] = {
 {
 	.name = "omap-mcbsp-dai",
 	.id = 0,
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 9965fd4b042708f581ac76f073864bf8479cc7c5..ed8afb55067173fc4969b2984ae20e10f5cabdea 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -44,6 +44,6 @@ enum omap_mcbsp_div {
  */
 #define NUM_LINKS	1
 
-extern struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS];
+extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS];
 
 #endif
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 62370202c649c5aac50e542626b574a7079909e2..e092f3d836d0a080a2579f4a93aa0237dd6cc1d3 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -316,7 +316,7 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
 	}
 }
 
-int omap_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
 		 struct snd_pcm *pcm)
 {
 	int ret = 0;
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 484f883459e086a95af2ff2edd881b8c8b9a0500..12f6ac99b04c4386b28f68cbcbe3d404b5cc2efe 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,6 +1,6 @@
 config SND_PXA2XX_SOC
 	tristate "SoC Audio for the Intel PXA2xx chip"
-	depends on ARCH_PXA && SND_SOC
+	depends on ARCH_PXA
 	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
@@ -62,3 +62,12 @@ config SND_PXA2XX_SOC_E800
 	help
 	  Say Y if you want to add support for SoC audio on the
 	  Toshiba e800 PDA
+
+config SND_PXA2XX_SOC_EM_X270
+	tristate "SoC Audio support for CompuLab EM-x270"
+	depends on SND_PXA2XX_SOC && MACH_EM_X270
+	select SND_PXA2XX_SOC_AC97
+	select SND_SOC_WM9712
+	help
+	  Say Y if you want to add support for SoC audio on
+	  CompuLab EM-x270.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 04e5646f75ba3f8d77cc5a0caf4d162439c87b53..5bc8edf9dca9ec8532ec6e56e1c7e7ea88f463fc 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -13,10 +13,11 @@ snd-soc-poodle-objs := poodle.o
 snd-soc-tosa-objs := tosa.o
 snd-soc-e800-objs := e800_wm9712.o
 snd-soc-spitz-objs := spitz.o
+snd-soc-em-x270-objs := em-x270.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
 obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
 obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
 obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
-
+obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 7f32a1167572c7cf67171a0468f0174cfb4da682..c0294464a23aef4f14c099b989dc058168b4da6a 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -11,10 +11,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    30th Nov 2005   Initial version.
- *
  */
 
 #include <linux/module.h>
@@ -54,47 +50,51 @@ static int corgi_spk_func;
 
 static void corgi_ext_control(struct snd_soc_codec *codec)
 {
-	int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
-
 	/* set up jack connection */
 	switch (corgi_jack_func) {
 	case CORGI_HP:
-		hp = 1;
 		/* set = unmute headphone */
 		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case CORGI_MIC:
-		mic = 1;
 		/* reset = mute headphone */
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case CORGI_LINE:
-		line = 1;
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_enable_pin(codec, "Line Jack");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case CORGI_HEADSET:
-		hs = 1;
-		mic = 1;
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_enable_pin(codec, "Headset Jack");
 		break;
 	}
 
 	if (corgi_spk_func == CORGI_SPK_ON)
-		spk = 1;
-
-	/* set the enpoints to their new connetion states */
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
-	snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
-	snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
-	snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	else
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
 
 	/* signal a DAPM event */
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int corgi_startup(struct snd_pcm_substream *substream)
@@ -123,8 +123,8 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	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;
 
@@ -143,25 +143,25 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set the I2S system clock as input (unused) */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -247,7 +247,7 @@ SND_SOC_DAPM_HP("Headset Jack", NULL),
 };
 
 /* Corgi machine audio map (connections to the codec pins) */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headset Jack  - in = micin, out = LHPOUT*/
 	{"Headset Jack", NULL, "LHPOUT"},
@@ -265,8 +265,6 @@ static const char *audio_map[][3] = {
 
 	/* Same as the above but no mic bias for line signals */
 	{"MICIN", NULL, "Line Jack"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -291,8 +289,8 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec)
 {
 	int i, err;
 
-	snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
+	snd_soc_dapm_disable_pin(codec, "LLINEIN");
+	snd_soc_dapm_disable_pin(codec, "RLINEIN");
 
 	/* Add corgi specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
@@ -303,15 +301,13 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec)
 	}
 
 	/* Add corgi specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+				  ARRAY_SIZE(wm8731_dapm_widgets));
 
 	/* Set up corgi specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
new file mode 100644
index 0000000000000000000000000000000000000000..02dcac39cdf6cc9bd71c4440a367092e7d2153f4
--- /dev/null
+++ b/sound/soc/pxa/em-x270.c
@@ -0,0 +1,102 @@
+/*
+ * em-x270.c  --  SoC audio for EM-X270
+ *
+ * Copyright 2007 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copied from tosa.c:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *          Richard Purdie <richard@openedhand.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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static struct snd_soc_dai_link em_x270_dai[] = {
+	{
+		.name = "AC97",
+		.stream_name = "AC97 HiFi",
+		.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+		.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+	},
+	{
+		.name = "AC97 Aux",
+		.stream_name = "AC97 Aux",
+		.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+		.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+	},
+};
+
+static struct snd_soc_machine em_x270 = {
+	.name = "EM-X270",
+	.dai_link = em_x270_dai,
+	.num_links = ARRAY_SIZE(em_x270_dai),
+};
+
+static struct snd_soc_device em_x270_snd_devdata = {
+	.machine = &em_x270,
+	.platform = &pxa2xx_soc_platform,
+	.codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *em_x270_snd_device;
+
+static int __init em_x270_init(void)
+{
+	int ret;
+
+	if (!machine_is_em_x270())
+		return -ENODEV;
+
+	em_x270_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!em_x270_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(em_x270_snd_device, &em_x270_snd_devdata);
+	em_x270_snd_devdata.dev = &em_x270_snd_device->dev;
+	ret = platform_device_add(em_x270_snd_device);
+
+	if (ret)
+		platform_device_put(em_x270_snd_device);
+
+	return ret;
+}
+
+static void __exit em_x270_exit(void)
+{
+	platform_device_unregister(em_x270_snd_device);
+}
+
+module_init(em_x270_init);
+module_exit(em_x270_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_DESCRIPTION("ALSA SoC EM-X270");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 7e830b218943e90090cebddbedd4cc6ec511bba8..65a4e9a8c39e18ec4dc0b45f44dd780ff6c54b36 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -48,8 +48,6 @@ static int poodle_spk_func;
 
 static void poodle_ext_control(struct snd_soc_codec *codec)
 {
-	int spk = 0;
-
 	/* set up jack connection */
 	if (poodle_jack_func == POODLE_HP) {
 		/* set = unmute headphone */
@@ -57,23 +55,23 @@ static void poodle_ext_control(struct snd_soc_codec *codec)
 			POODLE_LOCOMO_GPIO_MUTE_L, 1);
 		locomo_gpio_write(&poodle_locomo_device.dev,
 			POODLE_LOCOMO_GPIO_MUTE_R, 1);
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
 	} else {
 		locomo_gpio_write(&poodle_locomo_device.dev,
 			POODLE_LOCOMO_GPIO_MUTE_L, 0);
 		locomo_gpio_write(&poodle_locomo_device.dev,
 			POODLE_LOCOMO_GPIO_MUTE_R, 0);
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
 	}
 
-	if (poodle_spk_func == POODLE_SPK_ON)
-		spk = 1;
-
 	/* set the enpoints to their new connetion states */
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
+	if (poodle_spk_func == POODLE_SPK_ON)
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	else
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
 
 	/* signal a DAPM event */
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int poodle_startup(struct snd_pcm_substream *substream)
@@ -104,8 +102,8 @@ static int poodle_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	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;
 
@@ -124,25 +122,25 @@ static int poodle_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set the I2S system clock as input (unused) */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -215,8 +213,8 @@ SND_SOC_DAPM_HP("Headphone Jack", NULL),
 SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
 };
 
-/* Corgi machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+/* Corgi machine connections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headphone connected to LHPOUT1, RHPOUT1 */
 	{"Headphone Jack", NULL, "LHPOUT"},
@@ -225,8 +223,6 @@ static const char *audio_map[][3] = {
 	/* speaker connected to LOUT, ROUT */
 	{"Ext Spk", NULL, "ROUT"},
 	{"Ext Spk", NULL, "LOUT"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Off", "Headphone"};
@@ -250,9 +246,9 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec)
 {
 	int i, err;
 
-	snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "MICIN", 1);
+	snd_soc_dapm_disable_pin(codec, "LLINEIN");
+	snd_soc_dapm_disable_pin(codec, "RLINEIN");
+	snd_soc_dapm_enable_pin(codec, "MICIN");
 
 	/* Add poodle specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
@@ -263,15 +259,13 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec)
 	}
 
 	/* Add poodle specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+				  ARRAY_SIZE(wm8731_dapm_widgets));
 
 	/* Set up poodle specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 97ec2d90547c30e64a177f9f250168d02073d6c0..059af815ea0c65f449d31ea9aa73dee1c86a27ac 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -283,7 +283,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
 
 #ifdef CONFIG_PM
 static int pxa2xx_ac97_suspend(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	GCR |= GCR_ACLINK_OFF;
 	clk_disable(ac97_clk);
@@ -291,7 +291,7 @@ static int pxa2xx_ac97_suspend(struct platform_device *pdev,
 }
 
 static int pxa2xx_ac97_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
 	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
@@ -310,7 +310,8 @@ static int pxa2xx_ac97_resume(struct platform_device *pdev,
 #define pxa2xx_ac97_resume	NULL
 #endif
 
-static int pxa2xx_ac97_probe(struct platform_device *pdev)
+static int pxa2xx_ac97_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	int ret;
 
@@ -355,7 +356,8 @@ static int pxa2xx_ac97_probe(struct platform_device *pdev)
 	return ret;
 }
 
-static void pxa2xx_ac97_remove(struct platform_device *pdev)
+static void pxa2xx_ac97_remove(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
 {
 	GCR |= GCR_ACLINK_OFF;
 	free_irq(IRQ_AC97, NULL);
@@ -372,7 +374,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
 				struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out;
@@ -386,7 +388,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
@@ -400,7 +402,7 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		return -ENODEV;
@@ -418,7 +420,7 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
  * There is only 1 physical AC97 interface for pxa2xx, but it
  * has extra fifo's that can be used for aux DACs and ADCs.
  */
-struct snd_soc_cpu_dai pxa_ac97_dai[] = {
+struct snd_soc_dai pxa_ac97_dai[] = {
 {
 	.name = "pxa2xx-ac97",
 	.id = 0,
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
index b8ccfee095c4711ce17b0fc80176d5b9d409da28..e390de8edcd48724558f63c4338a9e83b19d1762 100644
--- a/sound/soc/pxa/pxa2xx-ac97.h
+++ b/sound/soc/pxa/pxa2xx-ac97.h
@@ -14,7 +14,7 @@
 #define PXA2XX_DAI_AC97_AUX		1
 #define PXA2XX_DAI_AC97_MIC		2
 
-extern struct snd_soc_cpu_dai pxa_ac97_dai[3];
+extern struct snd_soc_dai pxa_ac97_dai[3];
 
 /* platform data */
 extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 425071030970bf07fae6f50b3d863cf89d7c9022..9c06553b92674436619cd0261d19f410e6764f79 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -9,9 +9,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    12th Aug 2005   Initial version.
  */
 
 #include <linux/init.h>
@@ -80,7 +77,7 @@ static struct pxa2xx_gpio gpio_bus[] = {
 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (!cpu_dai->active) {
 		SACR0 |= SACR0_RST;
@@ -101,7 +98,7 @@ static int pxa_i2s_wait(void)
 	return 0;
 }
 
-static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
 	/* interface format */
@@ -127,7 +124,7 @@ static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
 	return 0;
 }
 
-static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	if (clk_id != PXA2XX_I2S_SYSCLK)
@@ -143,7 +140,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
 				struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
@@ -240,7 +237,7 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
 
 #ifdef CONFIG_PM
 static int pxa2xx_i2s_suspend(struct platform_device *dev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	if (!dai->active)
 		return 0;
@@ -258,7 +255,7 @@ static int pxa2xx_i2s_suspend(struct platform_device *dev,
 }
 
 static int pxa2xx_i2s_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	if (!dai->active)
 		return 0;
@@ -283,7 +280,7 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev,
 		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
 		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 
-struct snd_soc_cpu_dai pxa_i2s_dai = {
+struct snd_soc_dai pxa_i2s_dai = {
 	.name = "pxa2xx-i2s",
 	.id = 0,
 	.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h
index 4435bd9f884faec12ab5532c43f5ba687f638367..e2def441153eee706ab6789a00385c6e13f49284 100644
--- a/sound/soc/pxa/pxa2xx-i2s.h
+++ b/sound/soc/pxa/pxa2xx-i2s.h
@@ -15,6 +15,6 @@
 /* I2S clock */
 #define PXA2XX_I2S_SYSCLK		0
 
-extern struct snd_soc_cpu_dai pxa_i2s_dai;
+extern struct snd_soc_dai pxa_i2s_dai;
 
 #endif
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 01ad7bf716b7b03f95f99157cabece01d16d67bd..2df03ee5819ec7ae44b38e1e47955b1983fce247 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -330,7 +330,7 @@ static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
 static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
 
-int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
 	struct snd_pcm *pcm)
 {
 	int ret = 0;
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index d8b8372db00e76c9412364867025628f3b8d957e..64385797da5d3e0c959c737eaea442e22c0778f0 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -12,9 +12,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    30th Nov 2005   Initial version.
- *
  */
 
 #include <linux/module.h>
@@ -54,60 +51,60 @@ static int spitz_spk_func;
 static void spitz_ext_control(struct snd_soc_codec *codec)
 {
 	if (spitz_spk_func == SPITZ_SPK_ON)
-		snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
 	else
-		snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0);
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
 
 	/* set up jack connection */
 	switch (spitz_jack_func) {
 	case SPITZ_HP:
 		/* enable and unmute hp jack, disable mic bias */
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
 		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_MIC:
 		/* enable mic jack and bias, mute hp */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_LINE:
 		/* enable line jack, disable mic bias and mute hp */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_enable_pin(codec, "Line Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_HEADSET:
 		/* enable and unmute headset jack enable mic bias, mute L hp */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Headset Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_HP_OFF:
 
 		/* jack removed, everything off */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	}
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int spitz_startup(struct snd_pcm_substream *substream)
@@ -124,8 +121,8 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	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;
 
@@ -144,25 +141,25 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set the I2S system clock as input (unused) */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -250,7 +247,7 @@ static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
 };
 
 /* Spitz machine audio_map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headphone connected to LOUT1, ROUT1 */
 	{"Headphone Jack", NULL, "LOUT1"},
@@ -269,8 +266,6 @@ static const char *audio_map[][3] = {
 
 	/* line is connected to input 1 - no bias */
 	{"LINPUT1", NULL, "Line Jack"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -296,13 +291,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec)
 	int i, err;
 
 	/* NC codec pins */
-	snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0);
-	snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "MONO", 0);
+	snd_soc_dapm_disable_pin(codec, "RINPUT1");
+	snd_soc_dapm_disable_pin(codec, "LINPUT2");
+	snd_soc_dapm_disable_pin(codec, "RINPUT2");
+	snd_soc_dapm_disable_pin(codec, "LINPUT3");
+	snd_soc_dapm_disable_pin(codec, "RINPUT3");
+	snd_soc_dapm_disable_pin(codec, "OUT3");
+	snd_soc_dapm_disable_pin(codec, "MONO");
 
 	/* Add spitz specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
@@ -313,15 +308,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec)
 	}
 
 	/* Add spitz specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+				  ARRAY_SIZE(wm8750_dapm_widgets));
 
-	/* Set up spitz specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	/* Set up spitz specific audio paths */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index 7346d7e5d066e71b84669f0fe6c054e77b003ca1..b6edb61a3a30f8591e89944d002fb7eff1c253ac 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -12,9 +12,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    30th Nov 2005   Initial version.
- *
  * GPIO's
  *  1 - Jack Insertion
  *  5 - Hookswitch (headset answer/hang up switch)
@@ -55,29 +52,31 @@ static int tosa_spk_func;
 
 static void tosa_ext_control(struct snd_soc_codec *codec)
 {
-	int spk = 0, mic_int = 0, hp = 0, hs = 0;
-
 	/* set up jack connection */
 	switch (tosa_jack_func) {
 	case TOSA_HP:
-		hp = 1;
+		snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case TOSA_MIC_INT:
-		mic_int = 1;
+		snd_soc_dapm_enable_pin(codec, "Mic (Internal)");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case TOSA_HEADSET:
-		hs = 1;
+		snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_enable_pin(codec, "Headset Jack");
 		break;
 	}
 
 	if (tosa_spk_func == TOSA_SPK_ON)
-		spk = 1;
+		snd_soc_dapm_enable_pin(codec, "Speaker");
+	else
+		snd_soc_dapm_disable_pin(codec, "Speaker");
 
-	snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
-	snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
-	snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int tosa_startup(struct snd_pcm_substream *substream)
@@ -154,7 +153,7 @@ SND_SOC_DAPM_SPK("Speaker", NULL),
 };
 
 /* tosa audio map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headphone connected to HPOUTL, HPOUTR */
 	{"Headphone Jack", NULL, "HPOUTL"},
@@ -173,8 +172,6 @@ static const char *audio_map[][3] = {
 	{"Headset Jack", NULL, "HPOUTR"},
 	{"LINEINR", NULL, "Mic Bias"},
 	{"Mic Bias", NULL, "Headset Jack"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -196,8 +193,8 @@ static int tosa_ac97_init(struct snd_soc_codec *codec)
 {
 	int i, err;
 
-	snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
+	snd_soc_dapm_disable_pin(codec, "OUT3");
+	snd_soc_dapm_disable_pin(codec, "MONOOUT");
 
 	/* add tosa specific controls */
 	for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
@@ -208,17 +205,13 @@ static int tosa_ac97_init(struct snd_soc_codec *codec)
 	}
 
 	/* add tosa specific widgets */
-	for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
-		snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
-	}
+	snd_soc_dapm_new_controls(codec, tosa_dapm_widgets,
+				  ARRAY_SIZE(tosa_dapm_widgets));
 
 	/* set up tosa specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
-	}
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 1f6dbfc4caa85352412ba297e8094860e968fe00..b9f2353effebfba49cf5010c10bcf5b2794781c6 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,7 +1,6 @@
 config SND_S3C24XX_SOC
 	tristate "SoC Audio for the Samsung S3C24XX chips"
-	depends on ARCH_S3C2410 && SND_SOC
-	select SND_PCM
+	depends on ARCH_S3C2410
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the S3C24XX AC97, I2S or SSP interface. You will also need
@@ -16,7 +15,6 @@ config SND_S3C2412_SOC_I2S
 config SND_S3C2443_SOC_AC97
 	tristate
 	select AC97_BUS
-	select SND_AC97_CODEC
 	select SND_SOC_AC97_BUS
 	
 config SND_S3C24XX_SOC_NEO1973_WM8753
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 0e9d1c5f24840dab0d640d6f733aba13237742a2..4d7a9aa15f1a8cc20f41d390f5a0f87bcb35dcaf 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -10,10 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    20th Jan 2007   Initial version.
- *    05th Feb 2007   Rename all to Neo1973
- *
  */
 
 #include <linux/module.h>
@@ -26,6 +22,7 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/tlv.h>
 
 #include <asm/mach-types.h>
 #include <asm/hardware/scoop.h>
@@ -43,6 +40,14 @@
 #include "s3c24xx-pcm.h"
 #include "s3c24xx-i2s.h"
 
+/* Debugging stuff */
+#define S3C24XX_SOC_NEO1973_WM8753_DEBUG 0
+#if S3C24XX_SOC_NEO1973_WM8753_DEBUG
+#define DBG(x...) printk(KERN_DEBUG "s3c24xx-soc-neo1973-wm8753: " x)
+#else
+#define DBG(x...)
+#endif
+
 /* define the scenarios */
 #define NEO_AUDIO_OFF			0
 #define NEO_GSM_CALL_AUDIO_HANDSET	1
@@ -61,12 +66,14 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	unsigned int pll_out = 0, bclk = 0;
 	int ret = 0;
 	unsigned long iis_clkrate;
 
+	DBG("Entered %s\n", __func__);
+
 	iis_clkrate = s3c24xx_i2s_get_clockrate();
 
 	switch (params_rate(params)) {
@@ -101,44 +108,44 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai,
+	ret = snd_soc_dai_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,
+	ret = snd_soc_dai_set_fmt(cpu_dai,
 		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 		SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set MCLK division for sample rate */
-	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
 		S3C2410_IISMOD_32FS);
 	if (ret < 0)
 		return ret;
 
 	/* set codec BCLK division for sample rate */
-	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
 	if (ret < 0)
 		return ret;
 
 	/* set prescaler division for sample rate */
-	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 		S3C24XX_PRESCALE(4, 4));
 	if (ret < 0)
 		return ret;
 
 	/* codec PLL input is PCLK/4 */
-	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
 		iis_clkrate / 4, pll_out);
 	if (ret < 0)
 		return ret;
@@ -149,10 +156,12 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
 static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	DBG("Entered %s\n", __func__);
 
 	/* disable the PLL */
-	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
 }
 
 /*
@@ -167,11 +176,13 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 	unsigned int pcmdiv = 0;
 	int ret = 0;
 	unsigned long iis_clkrate;
 
+	DBG("Entered %s\n", __func__);
+
 	iis_clkrate = s3c24xx_i2s_get_clockrate();
 
 	if (params_rate(params) != 8000)
@@ -183,24 +194,24 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
 
 	/* todo: gg check mode (DSP_B) against CSR datasheet */
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set codec PCM division for sample rate */
-	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
 	if (ret < 0)
 		return ret;
 
 	/* configue and enable PLL for 12.288MHz output */
-	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
 		iis_clkrate / 4, 12288000);
 	if (ret < 0)
 		return ret;
@@ -211,10 +222,12 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
 static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	DBG("Entered %s\n", __func__);
 
 	/* disable the PLL */
-	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_voice_ops = {
@@ -233,79 +246,81 @@ static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
 
 static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
 {
+	DBG("Entered %s\n", __func__);
+
 	switch (neo1973_scenario) {
 	case NEO_AUDIO_OFF:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_GSM_CALL_AUDIO_HANDSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_enable_pin(codec, "Call Mic");
 		break;
 	case NEO_GSM_CALL_AUDIO_HEADSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line In");
+		snd_soc_dapm_enable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_GSM_CALL_AUDIO_BLUETOOTH:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_STEREO_TO_SPEAKERS:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_STEREO_TO_HEADPHONES:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_CAPTURE_HANDSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_enable_pin(codec, "Call Mic");
 		break;
 	case NEO_CAPTURE_HEADSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_enable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_CAPTURE_BLUETOOTH:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	default:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 	}
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
@@ -315,6 +330,8 @@ static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 
+	DBG("Entered %s\n", __func__);
+
 	if (neo1973_scenario == ucontrol->value.integer.value[0])
 		return 0;
 
@@ -327,6 +344,8 @@ static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
 
 static void lm4857_write_regs(void)
 {
+	DBG("Entered %s\n", __func__);
+
 	if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
 		printk(KERN_ERR "lm4857: i2c write failed\n");
 }
@@ -338,6 +357,8 @@ static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
 	int shift = (kcontrol->private_value >> 8) & 0x0F;
 	int mask = (kcontrol->private_value >> 16) & 0xFF;
 
+	DBG("Entered %s\n", __func__);
+
 	ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
 	return 0;
 }
@@ -364,6 +385,8 @@ static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
 {
 	u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
 
+	DBG("Entered %s\n", __func__);
+
 	if (value)
 		value -= 5;
 
@@ -376,6 +399,8 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
 {
 	u8 value = ucontrol->value.integer.value[0];
 
+	DBG("Entered %s\n", __func__);
+
 	if (value)
 		value += 5;
 
@@ -397,8 +422,7 @@ static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
 };
 
 
-/* example machine audio_mapnections */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route dapm_routes[] = {
 
 	/* Connections to the lm4857 amp */
 	{"Audio Out", NULL, "LOUT1"},
@@ -421,8 +445,6 @@ static const char *audio_map[][3] = {
 
 	/* Connect the ALC pins */
 	{"ACIN", NULL, "ACOP"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *lm4857_mode[] = {
@@ -453,13 +475,16 @@ static const struct soc_enum neo_scenario_enum[] = {
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios),
 };
 
+static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
+
 static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
-	SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
-		lm4857_get_reg, lm4857_set_reg),
-	SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
-		lm4857_get_reg, lm4857_set_reg),
-	SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
-		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+	SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+	SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg, mono_tlv),
 	SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
 		lm4857_get_mode, lm4857_set_mode),
 	SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
@@ -483,21 +508,23 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
 {
 	int i, err;
 
+	DBG("Entered %s\n", __func__);
+
 	/* set up NC codec pins */
-	snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "OUT3",  0);
-	snd_soc_dapm_set_endpoint(codec, "OUT4",  0);
-	snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
-	snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
+	snd_soc_dapm_disable_pin(codec, "LOUT2");
+	snd_soc_dapm_disable_pin(codec, "ROUT2");
+	snd_soc_dapm_disable_pin(codec, "OUT3");
+	snd_soc_dapm_disable_pin(codec, "OUT4");
+	snd_soc_dapm_disable_pin(codec, "LINE1");
+	snd_soc_dapm_disable_pin(codec, "LINE2");
 
 
 	/* set endpoints to default mode */
 	set_scenario_endpoints(codec, NEO_AUDIO_OFF);
 
 	/* Add neo1973 specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+				  ARRAY_SIZE(wm8753_dapm_widgets));
 
 	/* add neo1973 specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
@@ -508,20 +535,18 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
 			return err;
 	}
 
-	/* set up neo1973 specific audio path audio_mapnects */
-	for (i = 0; audio_map[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
-	}
+	/* set up neo1973 specific audio routes */
+	err = snd_soc_dapm_add_routes(codec, dapm_routes,
+				      ARRAY_SIZE(dapm_routes));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
 /*
  * BT Codec DAI
  */
-static struct snd_soc_cpu_dai bt_dai = {
+static struct snd_soc_dai bt_dai = {
 	.name = "Bluetooth",
 	.id = 0,
 	.type = SND_SOC_DAI_PCM,
@@ -583,6 +608,8 @@ static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
 {
 	int ret;
 
+	DBG("Entered %s\n", __func__);
+
 	client_template.adapter = adap;
 	client_template.addr = addr;
 
@@ -606,6 +633,8 @@ exit_err:
 
 static int lm4857_i2c_detach(struct i2c_client *client)
 {
+	DBG("Entered %s\n", __func__);
+
 	i2c_detach_client(client);
 	kfree(client);
 	return 0;
@@ -613,6 +642,8 @@ static int lm4857_i2c_detach(struct i2c_client *client)
 
 static int lm4857_i2c_attach(struct i2c_adapter *adap)
 {
+	DBG("Entered %s\n", __func__);
+
 	return i2c_probe(adap, &addr_data, lm4857_amp_probe);
 }
 
@@ -620,6 +651,8 @@ static u8 lm4857_state;
 
 static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
 {
+	DBG("Entered %s\n", __func__);
+
 	dev_dbg(&dev->dev, "lm4857_suspend\n");
 	lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
 	if (lm4857_state) {
@@ -631,6 +664,8 @@ static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
 
 static int lm4857_resume(struct i2c_client *dev)
 {
+	DBG("Entered %s\n", __func__);
+
 	if (lm4857_state) {
 		lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
 		lm4857_write_regs();
@@ -640,6 +675,8 @@ static int lm4857_resume(struct i2c_client *dev)
 
 static void lm4857_shutdown(struct i2c_client *dev)
 {
+	DBG("Entered %s\n", __func__);
+
 	dev_dbg(&dev->dev, "lm4857_shutdown\n");
 	lm4857_regs[LM4857_CTRL] &= 0xf0;
 	lm4857_write_regs();
@@ -671,6 +708,8 @@ static int __init neo1973_init(void)
 {
 	int ret;
 
+	DBG("Entered %s\n", __func__);
+
 	neo1973_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!neo1973_snd_device)
 		return -ENOMEM;
@@ -691,6 +730,8 @@ static int __init neo1973_init(void)
 
 static void __exit neo1973_exit(void)
 {
+	DBG("Entered %s\n", __func__);
+
 	i2c_del_driver(&lm4857_i2c_driver);
 	platform_device_unregister(neo1973_snd_device);
 }
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index c4a46dd589b3b1565f6c3d2e450465a3a4c6744b..ee4676ed1283c80729a0b1314f6730c82b2ab1b1 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -295,7 +295,7 @@ static inline int s3c2412_snd_is_clkmaster(void)
 /*
  * Set S3C2412 I2S DAI format
  */
-static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 			       unsigned int fmt)
 {
 	u32 iismod;
@@ -500,7 +500,7 @@ EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
 /*
  * Set S3C2412 Clock source
  */
-static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 				  int clk_id, unsigned int freq, int dir)
 {
 	u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
@@ -528,7 +528,7 @@ static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
 /*
  * Set S3C2412 Clock dividers
  */
-static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 				  int div_id, int div)
 {
 	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
@@ -601,7 +601,8 @@ struct clk *s3c2412_get_iisclk(void)
 EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
 
 
-static int s3c2412_i2s_probe(struct platform_device *pdev)
+static int s3c2412_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	DBG("Entered %s\n", __func__);
 
@@ -647,7 +648,7 @@ static int s3c2412_i2s_probe(struct platform_device *pdev)
 
 #ifdef CONFIG_PM
 static int s3c2412_i2s_suspend(struct platform_device *dev,
-			      struct snd_soc_cpu_dai *dai)
+			      struct snd_soc_dai *dai)
 {
 	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 	u32 iismod;
@@ -675,7 +676,7 @@ static int s3c2412_i2s_suspend(struct platform_device *dev,
 }
 
 static int s3c2412_i2s_resume(struct platform_device *pdev,
-			      struct snd_soc_cpu_dai *dai)
+			      struct snd_soc_dai *dai)
 {
 	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 
@@ -707,7 +708,7 @@ static int s3c2412_i2s_resume(struct platform_device *pdev,
 	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
-struct snd_soc_cpu_dai s3c2412_i2s_dai = {
+struct snd_soc_dai s3c2412_i2s_dai = {
 	.name	= "s3c2412-i2s",
 	.id	= 0,
 	.type	= SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
index 27f48e1ffa8610feb8e1d02740bd31bd607f59e9..aac08a25e541df2dc2cb6e0c0ebba094029105c7 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.h
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -24,7 +24,7 @@
 
 extern struct clk *s3c2412_get_iisclk(void);
 
-extern struct snd_soc_cpu_dai s3c2412_i2s_dai;
+extern struct snd_soc_dai s3c2412_i2s_dai;
 
 struct s3c2412_rate_calc {
 	unsigned int	clk_div;	/* for prescaler */
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index e81d9a6c83dac829f356d12d52c3539418f2aea8..783349b7fedec91aec58fd30d1f8838cd026cc04 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -10,9 +10,6 @@
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
- *
- *  Revision history
- *	21st Mar 2007   Initial Version
  */
 
 #include <linux/init.h>
@@ -212,7 +209,8 @@ static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
 	.dma_size	= 4,
 };
 
-static int s3c2443_ac97_probe(struct platform_device *pdev)
+static int s3c2443_ac97_probe(struct platform_device *pdev,
+			      struct snd_soc_dai *dai)
 {
 	int ret;
 	u32 ac_glbctrl;
@@ -263,7 +261,8 @@ static int s3c2443_ac97_probe(struct platform_device *pdev)
 	return ret;
 }
 
-static void s3c2443_ac97_remove(struct platform_device *pdev)
+static void s3c2443_ac97_remove(struct platform_device *pdev,
+				struct snd_soc_dai *dai)
 {
 	free_irq(IRQ_S3C244x_AC97, NULL);
 	clk_disable(s3c24xx_ac97.ac97_clk);
@@ -275,7 +274,7 @@ static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
 				struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
@@ -317,7 +316,7 @@ static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		return -ENODEV;
@@ -353,7 +352,7 @@ static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
 		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
 		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
 
-struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
+struct snd_soc_dai s3c2443_ac97_dai[] = {
 {
 	.name = "s3c2443-ac97",
 	.id = 0,
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
index bf03e8ed16c30a8b22b4cf5d37e151b12ffc6a00..a96dcadf28b4a0726cf91240d8b4440888cdfb6d 100644
--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
+++ b/sound/soc/s3c24xx/s3c24xx-ac97.h
@@ -26,6 +26,6 @@
 #define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
 #endif
 
-extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
+extern struct snd_soc_dai s3c2443_ac97_dai[];
 
 #endif /*S3C24XXAC97_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 1ed6afd454591ac9c5cab7b82b13b4105681826e..397524282b57f3ce865255d5bf7e3899df718be0 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -12,11 +12,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *
- *  Revision history
- *    11th Dec 2006   Merged with Simtec driver
- *    10th Nov 2006   Initial version.
  */
 
 #include <linux/init.h>
@@ -180,7 +175,7 @@ static void s3c24xx_snd_rxctrl(int on)
 static int s3c24xx_snd_lrsync(void)
 {
 	u32 iiscon;
-	unsigned long timeout = jiffies + msecs_to_jiffies(5);
+	int timeout = 50; /* 5ms */
 
 	DBG("Entered %s\n", __func__);
 
@@ -189,8 +184,9 @@ static int s3c24xx_snd_lrsync(void)
 		if (iiscon & S3C2410_IISCON_LRINDEX)
 			break;
 
-		if (time_after(jiffies, timeout))
+		if (!timeout--)
 			return -ETIMEDOUT;
+		udelay(100);
 	}
 
 	return 0;
@@ -209,7 +205,7 @@ static inline int s3c24xx_snd_is_clkmaster(void)
 /*
  * Set S3C24xx I2S DAI format
  */
-static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
 	u32 iismod;
@@ -317,7 +313,7 @@ exit_err:
 /*
  * Set S3C24xx Clock source
  */
-static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 	int clk_id, unsigned int freq, int dir)
 {
 	u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -343,7 +339,7 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
 /*
  * Set S3C24xx Clock dividers
  */
-static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 	int div_id, int div)
 {
 	u32 reg;
@@ -381,7 +377,8 @@ u32 s3c24xx_i2s_get_clockrate(void)
 }
 EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
 
-static int s3c24xx_i2s_probe(struct platform_device *pdev)
+static int s3c24xx_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	DBG("Entered %s\n", __func__);
 
@@ -414,7 +411,7 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev)
 
 #ifdef CONFIG_PM
 static int s3c24xx_i2s_suspend(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai)
+		struct snd_soc_dai *cpu_dai)
 {
 	DBG("Entered %s\n", __func__);
 
@@ -429,7 +426,7 @@ static int s3c24xx_i2s_suspend(struct platform_device *pdev,
 }
 
 static int s3c24xx_i2s_resume(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai)
+		struct snd_soc_dai *cpu_dai)
 {
 	DBG("Entered %s\n", __func__);
 	clk_enable(s3c24xx_i2s.iis_clk);
@@ -452,7 +449,7 @@ static int s3c24xx_i2s_resume(struct platform_device *pdev,
 	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
-struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
+struct snd_soc_dai s3c24xx_i2s_dai = {
 	.name = "s3c24xx-i2s",
 	.id = 0,
 	.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.h b/sound/soc/s3c24xx/s3c24xx-i2s.h
index 537b4ecce8a39e46847c442839d038282e4b8532..726d91cf4e1cfcbf624facdf7c0a697962bab190 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.h
@@ -32,6 +32,6 @@
 
 u32 s3c24xx_i2s_get_clockrate(void);
 
-extern struct snd_soc_cpu_dai s3c24xx_i2s_dai;
+extern struct snd_soc_dai s3c24xx_i2s_dai;
 
 #endif /*S3C24XXI2S_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 7806ae614617ca13f2c8c3325cf2daa800820f35..cef79b34dc6fc22e8789adef0e9c4be8e6ae8bbc 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -12,10 +12,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    11th Dec 2006   Merged with Simtec driver
- *    10th Nov 2006   Initial version.
  */
 
 #include <linux/module.h>
@@ -433,7 +429,7 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK;
 
 static int s3c24xx_pcm_new(struct snd_card *card,
-	struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+	struct snd_soc_dai *dai, struct snd_pcm *pcm)
 {
 	int ret = 0;
 
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index b4a56302b9ab091203427333c2174c35d4341ca2..8515d6ff03f2c50c5220f8a39cf9ec7a0389693d 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -10,9 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    8th Mar 2007   Initial version.
- *
  */
 
 #include <linux/module.h>
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 4c1e013381c9dbc1f7a70a38527f302334db1d34..54bd604012af6ce2d96fab4a111f7ba519292b2c 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -3,7 +3,7 @@ menu "SoC Audio support for SuperH"
 
 config SND_SOC_PCM_SH7760
 	tristate "SoC Audio support for Renesas SH7760"
-	depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
+	depends on CPU_SUBTYPE_SH7760 && SH_DMABRG
 	help
 	  Enable this option for SH7760 AC97/I2S audio support.
 
@@ -13,10 +13,9 @@ config SND_SOC_PCM_SH7760
 ##
 
 config SND_SOC_SH4_HAC
+	tristate
 	select AC97_BUS
 	select SND_SOC_AC97_BUS
-	select SND_AC97_CODEC
-	tristate
 
 config SND_SOC_SH4_SSI
 	tristate
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index 7a3ce80d6727dbe2daea6fa691ef0daff4974481..9faa12622d09c07895c48cdf5d96ff6cfd47e38e 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -326,7 +326,7 @@ static void camelot_pcm_free(struct snd_pcm *pcm)
 }
 
 static int camelot_pcm_new(struct snd_card *card,
-			   struct snd_soc_codec_dai *dai,
+			   struct snd_soc_dai *dai,
 			   struct snd_pcm *pcm)
 {
 	/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index b7b676b3d6715c38fd6196ce10162d245daca36d..df7bc345c3205cc0ae069322f6243e1027b71492 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -266,7 +266,7 @@ static int hac_hw_params(struct snd_pcm_substream *substream,
 #define AC97_FMTS	\
 	SNDRV_PCM_FMTBIT_S16_LE
 
-struct snd_soc_cpu_dai sh4_hac_dai[] = {
+struct snd_soc_dai sh4_hac_dai[] = {
 {
 	.name			= "HAC0",
 	.id			= 0,
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
index 2f91de84c5c762a2896c14230140c90b216d39a9..92bfaf4774a7d05fec84bf270539c8be09a6b05d 100644
--- a/sound/soc/sh/sh7760-ac97.c
+++ b/sound/soc/sh/sh7760-ac97.c
@@ -20,12 +20,12 @@
 #define IPSEL 0xFE400034
 
 /* platform specific structs can be declared here */
-extern struct snd_soc_cpu_dai sh4_hac_dai[2];
+extern struct snd_soc_dai sh4_hac_dai[2];
 extern struct snd_soc_platform sh7760_soc_platform;
 
 static int machine_init(struct snd_soc_codec *codec)
 {
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index 3388bc3d62d10fbef92a56c148ea4259ae2a6654..55c3464163ab45b50b7c359ef50b138c994bfcd1 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -208,7 +208,7 @@ static int ssi_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
+static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
 			  unsigned int freq, int dir)
 {
 	struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
@@ -222,7 +222,7 @@ static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
  * This divider is used to generate the SSI_SCK (I2S bitclock) from the
  * clock at the HAC_BIT_CLK ("oversampling clock") pin.
  */
-static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
+static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)
 {
 	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 	unsigned long ssicr;
@@ -245,7 +245,7 @@ static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
 	return 0;
 }
 
-static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
+static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
 	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 	unsigned long ssicr = SSIREG(SSICR);
@@ -332,7 +332,7 @@ static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
 	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\
 	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
 
-struct snd_soc_cpu_dai sh4_ssi_dai[] = {
+struct snd_soc_dai sh4_ssi_dai[] = {
 {
 	.name			= "SSI0",
 	.id			= 0,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e148db940cfc77ecb4521df7191909f00e672761..83f1190293a8287c2a516a81a07575dc7f4933c7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -14,10 +14,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    12th Aug 2005   Initial version.
- *    25th Oct 2005   Working Codec, Interface and Platform registration.
- *
  *  TODO:
  *   o Add hw rules to enforce rates, etc.
  *   o More testing with other codecs/machines.
@@ -112,9 +108,9 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
-static inline const char* get_dai_name(int type)
+static inline const char *get_dai_name(int type)
 {
-	switch(type) {
+	switch (type) {
 	case SND_SOC_DAI_AC97_BUS:
 	case SND_SOC_DAI_AC97:
 		return "AC97";
@@ -138,8 +134,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	int ret = 0;
 
 	mutex_lock(&pcm_mutex);
@@ -182,9 +178,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 	/* Check that the codec and cpu DAI's are compatible */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		runtime->hw.rate_min =
-			max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min);
+			max(codec_dai->playback.rate_min,
+			    cpu_dai->playback.rate_min);
 		runtime->hw.rate_max =
-			min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max);
+			min(codec_dai->playback.rate_max,
+			    cpu_dai->playback.rate_max);
 		runtime->hw.channels_min =
 			max(codec_dai->playback.channels_min,
 				cpu_dai->playback.channels_min);
@@ -197,9 +195,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 			codec_dai->playback.rates & cpu_dai->playback.rates;
 	} else {
 		runtime->hw.rate_min =
-			max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min);
+			max(codec_dai->capture.rate_min,
+			    cpu_dai->capture.rate_min);
 		runtime->hw.rate_max =
-			min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max);
+			min(codec_dai->capture.rate_max,
+			    cpu_dai->capture.rate_max);
 		runtime->hw.channels_min =
 			max(codec_dai->capture.channels_min,
 				cpu_dai->capture.channels_min);
@@ -229,7 +229,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 		goto machine_err;
 	}
 
-	dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name);
+	dbg("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
 	dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
 	dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
 		runtime->hw.channels_max);
@@ -272,11 +272,11 @@ static void close_delayed_work(struct work_struct *work)
 	struct snd_soc_device *socdev =
 		container_of(work, struct snd_soc_device, delayed_work.work);
 	struct snd_soc_codec *codec = socdev->codec;
-	struct snd_soc_codec_dai *codec_dai;
+	struct snd_soc_dai *codec_dai;
 	int i;
 
 	mutex_lock(&pcm_mutex);
-	for(i = 0; i < codec->num_dai; i++) {
+	for (i = 0; i < codec->num_dai; i++) {
 		codec_dai = &codec->dai[i];
 
 		dbg("pop wq checking: %s status: %s waiting: %s\n",
@@ -287,12 +287,12 @@ static void close_delayed_work(struct work_struct *work)
 		/* are we waiting on this codec DAI stream */
 		if (codec_dai->pop_wait == 1) {
 
-			/* power down the codec to D1 if no longer active */
+			/* Reduce power if no longer active */
 			if (codec->active == 0) {
 				dbg("pop wq D1 %s %s\n", codec->name,
 					codec_dai->playback.stream_name);
-				snd_soc_dapm_device_event(socdev,
-					SNDRV_CTL_POWER_D1);
+				snd_soc_dapm_set_bias_level(socdev,
+					SND_SOC_BIAS_PREPARE);
 			}
 
 			codec_dai->pop_wait = 0;
@@ -300,12 +300,12 @@ static void close_delayed_work(struct work_struct *work)
 				codec_dai->playback.stream_name,
 				SND_SOC_DAPM_STREAM_STOP);
 
-			/* power down the codec power domain if no longer active */
+			/* Fall into standby if no longer active */
 			if (codec->active == 0) {
 				dbg("pop wq D3 %s %s\n", codec->name,
 					codec_dai->playback.stream_name);
-				snd_soc_dapm_device_event(socdev,
-					SNDRV_CTL_POWER_D3hot);
+				snd_soc_dapm_set_bias_level(socdev,
+					SND_SOC_BIAS_STANDBY);
 			}
 		}
 	}
@@ -323,8 +323,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	struct snd_soc_codec *codec = socdev->codec;
 
 	mutex_lock(&pcm_mutex);
@@ -365,8 +365,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
 			SND_SOC_DAPM_STREAM_STOP);
 
 		if (codec->active == 0 && codec_dai->pop_wait == 0)
-			snd_soc_dapm_device_event(socdev,
-						SNDRV_CTL_POWER_D3hot);
+			snd_soc_dapm_set_bias_level(socdev,
+						SND_SOC_BIAS_STANDBY);
 	}
 
 	mutex_unlock(&pcm_mutex);
@@ -384,8 +384,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	struct snd_soc_codec *codec = socdev->codec;
 	int ret = 0;
 
@@ -434,14 +434,14 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
 		else {
 			codec_dai->pop_wait = 0;
 			cancel_delayed_work(&socdev->delayed_work);
-			if (codec_dai->dai_ops.digital_mute)
-				codec_dai->dai_ops.digital_mute(codec_dai, 0);
+			snd_soc_dai_digital_mute(codec_dai, 0);
 		}
 	} else {
 		/* no delayed work - do we need to power up codec */
-		if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
+		if (codec->bias_level != SND_SOC_BIAS_ON) {
 
-			snd_soc_dapm_device_event(socdev,  SNDRV_CTL_POWER_D1);
+			snd_soc_dapm_set_bias_level(socdev,
+						    SND_SOC_BIAS_PREPARE);
 
 			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 				snd_soc_dapm_stream_event(codec,
@@ -452,9 +452,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
 					codec_dai->capture.stream_name,
 					SND_SOC_DAPM_STREAM_START);
 
-			snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0);
-			if (codec_dai->dai_ops.digital_mute)
-				codec_dai->dai_ops.digital_mute(codec_dai, 0);
+			snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+			snd_soc_dai_digital_mute(codec_dai, 0);
 
 		} else {
 			/* codec already powered - power on widgets */
@@ -466,8 +465,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
 				snd_soc_dapm_stream_event(codec,
 					codec_dai->capture.stream_name,
 					SND_SOC_DAPM_STREAM_START);
-			if (codec_dai->dai_ops.digital_mute)
-				codec_dai->dai_ops.digital_mute(codec_dai, 0);
+
+			snd_soc_dai_digital_mute(codec_dai, 0);
 		}
 	}
 
@@ -488,8 +487,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	int ret = 0;
 
 	mutex_lock(&pcm_mutex);
@@ -514,7 +513,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
 	if (cpu_dai->ops.hw_params) {
 		ret = cpu_dai->ops.hw_params(substream, params);
 		if (ret < 0) {
-			printk(KERN_ERR "asoc: can't set interface %s hw params\n",
+			printk(KERN_ERR "asoc: interface %s hw params failed\n",
 				cpu_dai->name);
 			goto interface_err;
 		}
@@ -523,7 +522,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
 	if (platform->pcm_ops->hw_params) {
 		ret = platform->pcm_ops->hw_params(substream, params);
 		if (ret < 0) {
-			printk(KERN_ERR "asoc: can't set platform %s hw params\n",
+			printk(KERN_ERR "asoc: platform %s hw params failed\n",
 				platform->name);
 			goto platform_err;
 		}
@@ -542,7 +541,7 @@ interface_err:
 		codec_dai->ops.hw_free(substream);
 
 codec_err:
-	if(machine->ops && machine->ops->hw_free)
+	if (machine->ops && machine->ops->hw_free)
 		machine->ops->hw_free(substream);
 
 	mutex_unlock(&pcm_mutex);
@@ -558,15 +557,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	struct snd_soc_codec *codec = socdev->codec;
 
 	mutex_lock(&pcm_mutex);
 
 	/* apply codec digital mute */
-	if (!codec->active && codec_dai->dai_ops.digital_mute)
-		codec_dai->dai_ops.digital_mute(codec_dai, 1);
+	if (!codec->active)
+		snd_soc_dai_digital_mute(codec_dai, 1);
 
 	/* free any machine hw params */
 	if (machine->ops && machine->ops->hw_free)
@@ -593,8 +592,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	int ret;
 
 	if (codec_dai->ops.trigger) {
@@ -631,16 +630,26 @@ static struct snd_pcm_ops soc_pcm_ops = {
 /* powers down audio subsystem for suspend */
 static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 {
- 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- 	struct snd_soc_machine *machine = socdev->machine;
- 	struct snd_soc_platform *platform = socdev->platform;
- 	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_machine *machine = socdev->machine;
+	struct snd_soc_platform *platform = socdev->platform;
+	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 	struct snd_soc_codec *codec = socdev->codec;
 	int i;
 
+	/* Due to the resume being scheduled into a workqueue we could
+	* suspend before that's finished - wait for it to complete.
+	 */
+	snd_power_lock(codec->card);
+	snd_power_wait(codec->card, SNDRV_CTL_POWER_D0);
+	snd_power_unlock(codec->card);
+
+	/* we're going to block userspace touching us until resume completes */
+	snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
+
 	/* mute any active DAC's */
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
 		if (dai->dai_ops.digital_mute && dai->playback.active)
 			dai->dai_ops.digital_mute(dai, 1);
 	}
@@ -652,8 +661,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 	if (machine->suspend_pre)
 		machine->suspend_pre(pdev, state);
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai  *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai  *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
 			cpu_dai->suspend(pdev, cpu_dai);
 		if (platform->suspend)
@@ -662,9 +671,9 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 
 	/* close any waiting streams and save state */
 	run_delayed_work(&socdev->delayed_work);
-	codec->suspend_dapm_state = codec->dapm_state;
+	codec->suspend_bias_level = codec->bias_level;
 
-	for(i = 0; i < codec->num_dai; i++) {
+	for (i = 0; i < codec->num_dai; i++) {
 		char *stream = codec->dai[i].playback.stream_name;
 		if (stream != NULL)
 			snd_soc_dapm_stream_event(codec, stream,
@@ -678,8 +687,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 	if (codec_dev->suspend)
 		codec_dev->suspend(pdev, state);
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
 			cpu_dai->suspend(pdev, cpu_dai);
 	}
@@ -690,21 +699,32 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 	return 0;
 }
 
-/* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+/* deferred resume work, so resume can complete before we finished
+ * setting our codec back up, which can be very slow on I2C
+ */
+static void soc_resume_deferred(struct work_struct *work)
 {
- 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- 	struct snd_soc_machine *machine = socdev->machine;
- 	struct snd_soc_platform *platform = socdev->platform;
- 	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+	struct snd_soc_device *socdev = container_of(work,
+						     struct snd_soc_device,
+						     deferred_resume_work);
+	struct snd_soc_machine *machine = socdev->machine;
+	struct snd_soc_platform *platform = socdev->platform;
+	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 	struct snd_soc_codec *codec = socdev->codec;
+	struct platform_device *pdev = to_platform_device(socdev->dev);
 	int i;
 
+	/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
+	 * so userspace apps are blocked from touching us
+	 */
+
+	dev_info(socdev->dev, "starting resume work\n");
+
 	if (machine->resume_pre)
 		machine->resume_pre(pdev);
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
 			cpu_dai->resume(pdev, cpu_dai);
 	}
@@ -712,8 +732,8 @@ static int soc_resume(struct platform_device *pdev)
 	if (codec_dev->resume)
 		codec_dev->resume(pdev);
 
-	for(i = 0; i < codec->num_dai; i++) {
-		char* stream = codec->dai[i].playback.stream_name;
+	for (i = 0; i < codec->num_dai; i++) {
+		char *stream = codec->dai[i].playback.stream_name;
 		if (stream != NULL)
 			snd_soc_dapm_stream_event(codec, stream,
 				SND_SOC_DAPM_STREAM_RESUME);
@@ -723,15 +743,15 @@ static int soc_resume(struct platform_device *pdev)
 				SND_SOC_DAPM_STREAM_RESUME);
 	}
 
-	/* unmute any active DAC's */
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+	/* unmute any active DACs */
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
 		if (dai->dai_ops.digital_mute && dai->playback.active)
 			dai->dai_ops.digital_mute(dai, 0);
 	}
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
 			cpu_dai->resume(pdev, cpu_dai);
 		if (platform->resume)
@@ -741,6 +761,22 @@ static int soc_resume(struct platform_device *pdev)
 	if (machine->resume_post)
 		machine->resume_post(pdev);
 
+	dev_info(socdev->dev, "resume work completed\n");
+
+	/* userspace can access us now we are back as we were before */
+	snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
+}
+
+/* powers up audio subsystem after a suspend */
+static int soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	dev_info(socdev->dev, "scheduling resume work\n");
+
+	if (!schedule_work(&socdev->deferred_resume_work))
+		dev_err(socdev->dev, "work item may be lost\n");
+
 	return 0;
 }
 
@@ -760,33 +796,38 @@ static int soc_probe(struct platform_device *pdev)
 
 	if (machine->probe) {
 		ret = machine->probe(pdev);
-		if(ret < 0)
+		if (ret < 0)
 			return ret;
 	}
 
 	for (i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->probe) {
-			ret = cpu_dai->probe(pdev);
-			if(ret < 0)
+			ret = cpu_dai->probe(pdev, cpu_dai);
+			if (ret < 0)
 				goto cpu_dai_err;
 		}
 	}
 
 	if (codec_dev->probe) {
 		ret = codec_dev->probe(pdev);
-		if(ret < 0)
+		if (ret < 0)
 			goto cpu_dai_err;
 	}
 
 	if (platform->probe) {
 		ret = platform->probe(pdev);
-		if(ret < 0)
+		if (ret < 0)
 			goto platform_err;
 	}
 
 	/* DAPM stream work */
 	INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+#ifdef CONFIG_PM
+	/* deferred resume work */
+	INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
+#endif
+
 	return 0;
 
 platform_err:
@@ -795,9 +836,9 @@ platform_err:
 
 cpu_dai_err:
 	for (i--; i >= 0; i--) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->remove)
-			cpu_dai->remove(pdev);
+			cpu_dai->remove(pdev, cpu_dai);
 	}
 
 	if (machine->remove)
@@ -824,9 +865,9 @@ static int soc_remove(struct platform_device *pdev)
 		codec_dev->remove(pdev);
 
 	for (i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->remove)
-			cpu_dai->remove(pdev);
+			cpu_dai->remove(pdev, cpu_dai);
 	}
 
 	if (machine->remove)
@@ -852,8 +893,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
 	struct snd_soc_dai_link *dai_link, int num)
 {
 	struct snd_soc_codec *codec = socdev->codec;
-	struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai;
+	struct snd_soc_dai *codec_dai = dai_link->codec_dai;
+	struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
 	struct snd_soc_pcm_runtime *rtd;
 	struct snd_pcm *pcm;
 	char new_name[64];
@@ -868,7 +909,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
 	codec_dai->codec = socdev->codec;
 
 	/* check client and interface hw capabilities */
-	sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name,
+	sprintf(new_name, "%s %s-%s-%d", dai_link->stream_name, codec_dai->name,
 		get_dai_name(cpu_dai->type), num);
 
 	if (codec_dai->playback.channels_min)
@@ -879,7 +920,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
 	ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,
 		capture, &pcm);
 	if (ret < 0) {
-		printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+		printk(KERN_ERR "asoc: can't create pcm for codec %s\n",
+			codec->name);
 		kfree(rtd);
 		return ret;
 	}
@@ -928,8 +970,9 @@ static ssize_t codec_reg_show(struct device *dev,
 		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: %4x\n", i,
+			codec->read(codec, i));
 
 	return count;
 }
@@ -1072,7 +1115,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
 	strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
 
 	/* create the pcms */
-	for(i = 0; i < machine->num_links; i++) {
+	for (i = 0; i < machine->num_links; i++) {
 		ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
 		if (ret < 0) {
 			printk(KERN_ERR "asoc: can't create pcm %s\n",
@@ -1102,7 +1145,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
 	struct snd_soc_machine *machine = socdev->machine;
 	int ret = 0, i, ac97 = 0, err = 0;
 
-	for(i = 0; i < machine->num_links; i++) {
+	for (i = 0; i < machine->num_links; i++) {
 		if (socdev->machine->dai_link[i].init) {
 			err = socdev->machine->dai_link[i].init(codec);
 			if (err < 0) {
@@ -1111,7 +1154,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
 				continue;
 			}
 		}
-		if (socdev->machine->dai_link[i].codec_dai->type == 
+		if (socdev->machine->dai_link[i].codec_dai->type ==
 			SND_SOC_DAI_AC97_BUS)
 			ac97 = 1;
 	}
@@ -1122,7 +1165,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
 
 	ret = snd_card_register(codec->card);
 	if (ret < 0) {
-		printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n",
+		printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
 				codec->name);
 		goto out;
 	}
@@ -1146,7 +1189,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
 
 	err = device_create_file(socdev->dev, &dev_attr_codec_reg);
 	if (err < 0)
-		printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
+		printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
 
 	mutex_unlock(&codec->mutex);
 
@@ -1166,13 +1209,13 @@ void snd_soc_free_pcms(struct snd_soc_device *socdev)
 {
 	struct snd_soc_codec *codec = socdev->codec;
 #ifdef CONFIG_SND_SOC_AC97_BUS
-	struct snd_soc_codec_dai *codec_dai;
+	struct snd_soc_dai *codec_dai;
 	int i;
 #endif
 
 	mutex_lock(&codec->mutex);
 #ifdef CONFIG_SND_SOC_AC97_BUS
-	for(i = 0; i < codec->num_dai; i++) {
+	for (i = 0; i < codec->num_dai; i++) {
 		codec_dai = &codec->dai[i];
 		if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) {
 			soc_ac97_dev_unregister(codec);
@@ -1282,7 +1325,8 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
 	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
 		;
 	val = snd_soc_read(codec, e->reg);
-	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+	ucontrol->value.enumerated.item[0]
+		= (val >> e->shift_l) & (bitmask - 1);
 	if (e->shift_l != e->shift_r)
 		ucontrol->value.enumerated.item[1] =
 			(val >> e->shift_r) & (bitmask - 1);
@@ -1576,7 +1620,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
 	val = val << shift;
 	val2 = val2 << shift;
 
-	if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0)
+	err = snd_soc_update_bits(codec, reg, val_mask, val);
+	if (err < 0)
 		return err;
 
 	err = snd_soc_update_bits(codec, reg2, val_mask, val2);
@@ -1584,6 +1629,204 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
 
+/**
+ * snd_soc_info_volsw_s8 - signed mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+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);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = max-min;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
+
+/**
+ * snd_soc_get_volsw_s8 - signed mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_s8(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 min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+	int val = snd_soc_read(codec, reg);
+
+	ucontrol->value.integer.value[0] =
+		((signed char)(val & 0xff))-min;
+	ucontrol->value.integer.value[1] =
+		((signed char)((val >> 8) & 0xff))-min;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
+
+/**
+ * snd_soc_put_volsw_sgn - signed mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_s8(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 min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+	unsigned short val;
+
+	val = (ucontrol->value.integer.value[0]+min) & 0xff;
+	val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
+
+	return snd_soc_update_bits(codec, reg, 0xffff, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
+
+/**
+ * snd_soc_dai_set_sysclk - configure DAI system or master clock.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
+ *
+ * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
+ */
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+	unsigned int freq, int dir)
+{
+	if (dai->dai_ops.set_sysclk)
+		return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
+
+/**
+ * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
+ * @dai: DAI
+ * @clk_id: DAI specific clock divider ID
+ * @div: new clock divisor.
+ *
+ * Configures the clock dividers. This is used to derive the best DAI bit and
+ * frame clocks from the system or master clock. It's best to set the DAI bit
+ * and frame clocks as low as possible to save system power.
+ */
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+	int div_id, int div)
+{
+	if (dai->dai_ops.set_clkdiv)
+		return dai->dai_ops.set_clkdiv(dai, div_id, div);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
+
+/**
+ * snd_soc_dai_set_pll - configure DAI PLL.
+ * @dai: DAI
+ * @pll_id: DAI specific PLL ID
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
+ *
+ * Configures and enables PLL to generate output clock based on input clock.
+ */
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+	int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	if (dai->dai_ops.set_pll)
+		return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
+
+/**
+ * snd_soc_dai_set_fmt - configure DAI hardware audio format.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @fmt: SND_SOC_DAIFMT_ format value.
+ *
+ * Configures the DAI hardware format and clocking.
+ */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	if (dai->dai_ops.set_fmt)
+		return dai->dai_ops.set_fmt(dai, fmt);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
+
+/**
+ * snd_soc_dai_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @mask: DAI specific mask representing used slots.
+ * @slots: Number of slots in use.
+ *
+ * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
+ * specific.
+ */
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int mask, int slots)
+{
+	if (dai->dai_ops.set_sysclk)
+		return dai->dai_ops.set_tdm_slot(dai, mask, slots);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
+
+/**
+ * snd_soc_dai_set_tristate - configure DAI system or master clock.
+ * @dai: DAI
+ * @tristate: tristate enable
+ *
+ * Tristates the DAI so that others can use it.
+ */
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	if (dai->dai_ops.set_sysclk)
+		return dai->dai_ops.set_tristate(dai, tristate);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
+
+/**
+ * snd_soc_dai_digital_mute - configure DAI system or master clock.
+ * @dai: DAI
+ * @mute: mute enable
+ *
+ * Mutes the DAI DAC.
+ */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	if (dai->dai_ops.digital_mute)
+		return dai->dai_ops.digital_mute(dai, mute);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
+
 static int __devinit snd_soc_init(void)
 {
 	printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
@@ -1592,7 +1835,7 @@ static int __devinit snd_soc_init(void)
 
 static void snd_soc_exit(void)
 {
- 	platform_driver_unregister(&soc_driver);
+	platform_driver_unregister(&soc_driver);
 }
 
 module_init(snd_soc_init);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index af3326c635041da0c852ecc0ca11ea91212484f1..2c87061c2a6b4d7b9c35d7479d782462a0d8b3c1 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -10,11 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    12th Aug 2005   Initial version.
- *    25th Oct 2005   Implemented path power domain.
- *    18th Dec 2005   Implemented machine and stream level power domain.
- *
  *  Features:
  *    o Changes power status of internal codec blocks depending on the
  *      dynamic configuration of codec internal audio paths and active
@@ -50,23 +45,10 @@
 #include <sound/initval.h>
 
 /* debug */
-#define DAPM_DEBUG 0
-#if DAPM_DEBUG
+#ifdef DEBUG
 #define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
-#define dbg(format, arg...) printk(format, ## arg)
 #else
 #define dump_dapm(codec, action)
-#define dbg(format, arg...)
-#endif
-
-#define POP_DEBUG 0
-#if POP_DEBUG
-#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
-#define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
-#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
-#else
-#define pop_dbg(format, arg...)
-#define pop_wait(time)
 #endif
 
 /* dapm power sequences - make this per codec in the future */
@@ -85,6 +67,28 @@ static int dapm_status = 1;
 module_param(dapm_status, int, 0);
 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
 
+static unsigned int pop_time;
+
+static void pop_wait(void)
+{
+	if (pop_time)
+		schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
+}
+
+static void pop_dbg(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+
+	if (pop_time) {
+		vprintk(fmt, args);
+		pop_wait();
+	}
+
+	va_end(args);
+}
+
 /* create a new dapm widget */
 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
 	const struct snd_soc_dapm_widget *_widget)
@@ -222,11 +226,12 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
 	change = old != new;
 	if (change) {
 		pop_dbg("pop test %s : %s in %d ms\n", widget->name,
-			widget->power ? "on" : "off", POP_TIME);
+			widget->power ? "on" : "off", pop_time);
 		snd_soc_write(codec, widget->reg, new);
-		pop_wait(POP_TIME);
+		pop_wait();
 	}
-	dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change);
+	pr_debug("reg %x old %x new %x change %d\n", widget->reg,
+		 old, new, change);
 	return change;
 }
 
@@ -447,6 +452,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
 	return con;
 }
 
+/*
+ * Handler for generic register modifier widget.
+ */
+int dapm_reg_event(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol, int event)
+{
+	unsigned int val;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		val = w->on_val;
+	else
+		val = w->off_val;
+
+	snd_soc_update_bits(w->codec, -(w->reg + 1),
+			    w->mask << w->shift, val << w->shift);
+
+	return 0;
+}
+
 /*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
@@ -565,8 +589,8 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 			/* call any power change event handlers */
 			if (power_change) {
 				if (w->event) {
-					dbg("power %s event for %s flags %x\n",
-						w->power ? "on" : "off", w->name, w->event_flags);
+					pr_debug("power %s event for %s flags %x\n",
+						 w->power ? "on" : "off", w->name, w->event_flags);
 					if (power) {
 						/* power up event */
 						if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
@@ -608,7 +632,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 	return ret;
 }
 
-#if DAPM_DEBUG
+#ifdef DEBUG
 static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
 {
 	struct snd_soc_dapm_widget *w;
@@ -693,8 +717,10 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
 			path->connect = 0; /* old connection must be powered down */
 	}
 
-	if (found)
+	if (found) {
 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+		dump_dapm(widget->codec, "mux power update");
+	}
 
 	return 0;
 }
@@ -730,8 +756,10 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
 		break;
 	}
 
-	if (found)
+	if (found) {
 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+		dump_dapm(widget->codec, "mixer power update");
+	}
 
 	return 0;
 }
@@ -768,21 +796,18 @@ static ssize_t dapm_widget_show(struct device *dev,
 		}
 	}
 
-	switch(codec->dapm_state){
-	case SNDRV_CTL_POWER_D0:
-		state = "D0";
+	switch (codec->bias_level) {
+	case SND_SOC_BIAS_ON:
+		state = "On";
 		break;
-	case SNDRV_CTL_POWER_D1:
-		state = "D1";
+	case SND_SOC_BIAS_PREPARE:
+		state = "Prepare";
 		break;
-	case SNDRV_CTL_POWER_D2:
-		state = "D2";
+	case SND_SOC_BIAS_STANDBY:
+		state = "Standby";
 		break;
-	case SNDRV_CTL_POWER_D3hot:
-		state = "D3hot";
-		break;
-	case SNDRV_CTL_POWER_D3cold:
-		state = "D3cold";
+	case SND_SOC_BIAS_OFF:
+		state = "Off";
 		break;
 	}
 	count += sprintf(buf + count, "PM State: %s\n", state);
@@ -792,20 +817,51 @@ static ssize_t dapm_widget_show(struct device *dev,
 
 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)
+	if (dapm_status) {
 		ret = device_create_file(dev, &dev_attr_dapm_widget);
 
+		if (ret == 0)
+			ret = device_create_file(dev, &dev_attr_dapm_pop_time);
+	}
+
 	return ret;
 }
 
 static void snd_soc_dapm_sys_remove(struct device *dev)
 {
-	if (dapm_status)
+	if (dapm_status) {
+		device_remove_file(dev, &dev_attr_dapm_pop_time);
 		device_remove_file(dev, &dev_attr_dapm_widget);
+	}
 }
 
 /* free all dapm widgets and resources */
@@ -826,8 +882,25 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
 	}
 }
 
+static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
+	char *pin, int status)
+{
+	struct snd_soc_dapm_widget *w;
+
+	list_for_each_entry(w, &codec->dapm_widgets, list) {
+		if (!strcmp(w->name, pin)) {
+			pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+			w->connected = status;
+			return 0;
+		}
+	}
+
+	pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+	return -EINVAL;
+}
+
 /**
- * snd_soc_dapm_sync_endpoints - scan and power dapm paths
+ * snd_soc_dapm_sync - scan and power dapm paths
  * @codec: audio codec
  *
  * Walks all dapm audio paths and powers widgets according to their
@@ -835,27 +908,16 @@ static void dapm_free_widgets(struct snd_soc_codec *codec)
  *
  * Returns 0 for success.
  */
-int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
+int snd_soc_dapm_sync(struct snd_soc_codec *codec)
 {
-	return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+	int ret = dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+	dump_dapm(codec, "sync");
+	return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
-/**
- * snd_soc_dapm_connect_input - connect dapm widgets
- * @codec: audio codec
- * @sink: name of target widget
- * @control: mixer control name
- * @source: name of source name
- *
- * Connects 2 dapm widgets together via a named audio path. The sink is
- * the widget receiving the audio signal, whilst the source is the sender
- * of the audio signal.
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
-	const char * control, const char *source)
+static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
+	const char *sink, const char *control, const char *source)
 {
 	struct snd_soc_dapm_path *path;
 	struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
@@ -957,8 +1019,63 @@ err:
 	kfree(path);
 	return ret;
 }
+
+/**
+ * snd_soc_dapm_connect_input - connect dapm widgets
+ * @codec: audio codec
+ * @sink: name of target widget
+ * @control: mixer control name
+ * @source: name of source name
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * This function has been deprecated in favour of snd_soc_dapm_add_routes().
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
+	const char *control, const char *source)
+{
+	return snd_soc_dapm_add_route(codec, sink, control, source);
+}
 EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
 
+/**
+ * snd_soc_dapm_add_routes - Add routes between DAPM widgets
+ * @codec: codec
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * Returns 0 for success else error. On error all resources can be freed
+ * with a call to snd_soc_card_free().
+ */
+int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+			    const struct snd_soc_dapm_route *route, int num)
+{
+	int i, ret;
+
+	for (i = 0; i < num; i++) {
+		ret = snd_soc_dapm_add_route(codec, route->sink,
+					     route->control, route->source);
+		if (ret < 0) {
+			printk(KERN_ERR "Failed to add route %s->%s\n",
+			       route->source,
+			       route->sink);
+			return ret;
+		}
+		route++;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
+
 /**
  * snd_soc_dapm_new_widgets - add new dapm widgets
  * @codec: audio codec
@@ -1233,6 +1350,33 @@ int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
 
+/**
+ * snd_soc_dapm_new_controls - create new dapm controls
+ * @codec: audio codec
+ * @widget: widget array
+ * @num: number of widgets
+ *
+ * Creates new DAPM controls based upon the templates.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+	const struct snd_soc_dapm_widget *widget,
+	int num)
+{
+	int i, ret;
+
+	for (i = 0; i < num; i++) {
+		ret = snd_soc_dapm_new_control(codec, widget);
+		if (ret < 0)
+			return ret;
+		widget++;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
+
+
 /**
  * snd_soc_dapm_stream_event - send a stream event to the dapm core
  * @codec: audio codec
@@ -1257,8 +1401,8 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
 	{
 		if (!w->sname)
 			continue;
-		dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
-			stream, event);
+		pr_debug("widget %s\n %s stream %s event %d\n",
+			 w->name, w->sname, stream, event);
 		if (strstr(w->sname, stream)) {
 			switch(event) {
 			case SND_SOC_DAPM_STREAM_START:
@@ -1294,53 +1438,81 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
 /**
- * snd_soc_dapm_device_event - send a device event to the dapm core
+ * snd_soc_dapm_set_bias_level - set the bias level for the system
  * @socdev: audio device
- * @event: device event
+ * @level: level to configure
  *
- * Sends a device event to the dapm core. The core then makes any
- * necessary machine or codec power changes..
+ * Configure the bias (power) levels for the SoC audio device.
  *
  * Returns 0 for success else error.
  */
-int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
+int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+				enum snd_soc_bias_level level)
 {
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_machine *machine = socdev->machine;
+	int ret = 0;
 
-	if (machine->dapm_event)
-		machine->dapm_event(machine, event);
-	if (codec->dapm_event)
-		codec->dapm_event(codec, event);
-	return 0;
+	if (machine->set_bias_level)
+		ret = machine->set_bias_level(machine, level);
+	if (ret == 0 && codec->set_bias_level)
+		ret = codec->set_bias_level(codec, level);
+
+	return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
 
 /**
- * snd_soc_dapm_set_endpoint - set audio endpoint status
+ * snd_soc_dapm_enable_pin - enable pin.
+ * @snd_soc_codec: SoC codec
+ * @pin: pin name
+ *
+ * Enables input/output pin and it's parents or children widgets iff there is
+ * a valid audio route and active audio stream.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin)
+{
+	return snd_soc_dapm_set_pin(codec, pin, 1);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
+
+/**
+ * snd_soc_dapm_disable_pin - disable pin.
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Disables input/output pin and it's parents or children widgets.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin)
+{
+	return snd_soc_dapm_set_pin(codec, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
+
+/**
+ * snd_soc_dapm_get_pin_status - get audio pin status
  * @codec: audio codec
- * @endpoint: audio signal endpoint (or start point)
- * @status: point status
+ * @pin: audio signal pin endpoint (or start point)
  *
- * Set audio endpoint status - connected or disconnected.
+ * Get audio pin status - connected or disconnected.
  *
- * Returns 0 for success else error.
+ * Returns 1 for connected otherwise 0.
  */
-int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
-	char *endpoint, int status)
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin)
 {
 	struct snd_soc_dapm_widget *w;
 
 	list_for_each_entry(w, &codec->dapm_widgets, list) {
-		if (!strcmp(w->name, endpoint)) {
-			w->connected = status;
-			return 0;
-		}
+		if (!strcmp(w->name, pin))
+			return w->connected;
 	}
 
-	return -ENODEV;
+	return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
 
 /**
  * snd_soc_dapm_free - free dapm resources
diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig
index 079e22af074c08cf45bdc0bc0559f327869dcce8..d75deba5617db05dc02407b6c669d61ee1695a5e 100644
--- a/sound/sparc/Kconfig
+++ b/sound/sparc/Kconfig
@@ -1,11 +1,17 @@
 # ALSA Sparc drivers
 
-menu "ALSA Sparc devices"
-	depends on SND!=n && SPARC
+menuconfig SND_SPARC
+	bool "Sparc sound devices"
+	depends on SPARC
+	default y
+	help
+	  Support for sound devices specific to Sun SPARC architectures.
+
+if SND_SPARC
 
 config SND_SUN_AMD7930
 	tristate "Sun AMD7930"
-	depends on SBUS && SND
+	depends on SBUS
 	select SND_PCM
 	help
 	  Say Y here to include support for AMD7930 sound device on Sun.
@@ -15,7 +21,6 @@ config SND_SUN_AMD7930
 
 config SND_SUN_CS4231
 	tristate "Sun CS4231"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include support for CS4231 sound device on Sun.
@@ -25,7 +30,7 @@ config SND_SUN_CS4231
 
 config SND_SUN_DBRI
 	tristate "Sun DBRI"
-	depends on SND && SBUS
+	depends on SBUS
 	select SND_PCM
 	help
 	  Say Y here to include support for DBRI sound device on Sun.
@@ -33,4 +38,4 @@ config SND_SUN_DBRI
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-sun-dbri.
 
-endmenu
+endif	# SND_SPARC
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 3d00e0797b11899042e5af98fa64a224ebc13597..ee2e1b4f35514dfa999dabcccfb689bf9ecd077d 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -2490,7 +2490,7 @@ static void dbri_debug_read(struct snd_info_entry *entry,
 }
 #endif
 
-void __devinit snd_dbri_proc(struct snd_card *card)
+static void __devinit snd_dbri_proc(struct snd_card *card)
 {
 	struct snd_dbri *dbri = card->private_data;
 	struct snd_info_entry *entry;
diff --git a/sound/spi/Kconfig b/sound/spi/Kconfig
index 0d08c29213c8ffa9db4ceea7195ba01e1c746053..e6485be2e6f7a70c1762041ca89e323eb5c5c3e4 100644
--- a/sound/spi/Kconfig
+++ b/sound/spi/Kconfig
@@ -1,7 +1,13 @@
 #SPI drivers
 
-menu "SPI devices"
-	depends on SND != n
+menuconfig SND_SPI
+	bool "SPI sound devices"
+	depends on SPI
+	default y
+	help
+	  Support for sound devices connected via the SPI bus.
+
+if SND_SPI
 
 config SND_AT73C213
 	tristate "Atmel AT73C213 DAC driver"
@@ -28,4 +34,5 @@ config SND_AT73C213_TARGET_BITRATE
 
 	  Set to 48000 Hz by default.
 
-endmenu
+endif	# SND_SPI
+
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 9351b8a765b9c6de7f5069e2c3c6d09b0f54a50d..ffcdc8f4ef66de645e41ffa2881d12fc1ce5e0e9 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -1,11 +1,16 @@
 # ALSA USB drivers
 
-menu "USB devices"
-	depends on SND!=n && USB!=n
+menuconfig SND_USB
+	bool "USB sound devices"
+	depends on USB
+	default y
+	help
+	  Support for sound devices connected via the USB bus.
+
+if SND_USB && USB
 
 config SND_USB_AUDIO
 	tristate "USB Audio/MIDI driver"
-	depends on SND && USB
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -18,7 +23,7 @@ config SND_USB_AUDIO
 
 config SND_USB_USX2Y
 	tristate "Tascam US-122, US-224 and US-428 USB driver"
-	depends on SND && USB && (X86 || PPC || ALPHA)
+	depends on X86 || PPC || ALPHA
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -31,7 +36,6 @@ config SND_USB_USX2Y
 
 config SND_USB_CAIAQ
 	tristate "Native Instruments USB audio devices"
-	depends on SND && USB
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -63,5 +67,5 @@ config SND_USB_CAIAQ_INPUT
 	   * Native Instruments Kore Controller 2
 	   * Native Instruments Audio Kontrol 1
 
-endmenu
+endif	# SND_USB
 
diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c
index 24970a5c888f6e2f26453712a3b12cfd544b5355..b3a60332583534735cfd54504cc8141cdb4587cd 100644
--- a/sound/usb/caiaq/caiaq-audio.c
+++ b/sound/usb/caiaq/caiaq-audio.c
@@ -637,6 +637,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev)
 	switch (dev->chip.usb_id) {
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO):
 		dev->samplerates |= SNDRV_PCM_RATE_88200;
 		dev->samplerates |= SNDRV_PCM_RATE_192000;
 		break;
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index a972f77bd7856e6d9bf400f79923bb7d1ecafebc..83175083e50f8c60852cc0250fd26ace2038afba 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -42,14 +42,15 @@
 #endif
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.6");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.8");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
 			 "{Native Instruments, RigKontrol3},"
 			 "{Native Instruments, Kore Controller},"
 			 "{Native Instruments, Kore Controller 2},"
-			 "{Native Instruments, Audio Kontrol 1}"
-			 "{Native Instruments, Audio 8 DJ}}");
+			 "{Native Instruments, Audio Kontrol 1},"
+			 "{Native Instruments, Audio 8 DJ},"
+			 "{Native Instruments, Session I/O}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
 static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -110,6 +111,11 @@ static struct usb_device_id snd_usb_id_table[] = {
 		.idVendor =     USB_VID_NATIVEINSTRUMENTS,
 		.idProduct =    USB_PID_AUDIO8DJ
 	},
+	{
+		.match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+		.idVendor =     USB_VID_NATIVEINSTRUMENTS,
+		.idProduct =    USB_PID_SESSIONIO
+	},
 	{ /* terminator */ }
 };
 
diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h
index 96a491379c6062b540ff2a063f64735f1e2f640e..f9fbdbae269d21491d2dc84f726cfc12cdf51261 100644
--- a/sound/usb/caiaq/caiaq-device.h
+++ b/sound/usb/caiaq/caiaq-device.h
@@ -11,6 +11,7 @@
 #define USB_PID_KORECONTROLLER2	0x4712
 #define USB_PID_AK1		0x0815
 #define USB_PID_AUDIO8DJ	0x1978
+#define USB_PID_SESSIONIO	0x1915
 
 #define EP1_BUFSIZE 64
 #define CAIAQ_USB_STR_LEN 0xff
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 410be4aff1baed739dac2d88fa178d58f640e993..b8cfb7c2276872a05f40a07a0ed57d0b57357e8e 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -819,10 +819,6 @@ static const char *usb_error_string(int err)
 		return "device disabled";
 	case -EHOSTUNREACH:
 		return "device suspended";
-#ifndef CONFIG_USB_EHCI_SPLIT_ISO
-	case -ENOSYS:
-		return "enable CONFIG_USB_EHCI_SPLIT_ISO to play through a hub";
-#endif
 	case -EINVAL:
 	case -EAGAIN:
 	case -EFBIG:
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 82a8d14c26af26671513990291db6426c2de05c4..9ea726c049c6c1f5d7b891e2e25bcd62ff934e40 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -210,6 +210,11 @@ YAMAHA_DEVICE(0x1042, NULL),
 YAMAHA_DEVICE(0x1043, NULL),
 YAMAHA_DEVICE(0x1044, NULL),
 YAMAHA_DEVICE(0x1045, NULL),
+YAMAHA_INTERFACE(0x104e, 0, NULL),
+YAMAHA_DEVICE(0x104f, NULL),
+YAMAHA_DEVICE(0x1050, NULL),
+YAMAHA_DEVICE(0x1051, NULL),
+YAMAHA_DEVICE(0x1052, NULL),
 YAMAHA_DEVICE(0x2000, "DGP-7"),
 YAMAHA_DEVICE(0x2001, "DGP-5"),
 YAMAHA_DEVICE(0x2002, NULL),
@@ -1379,6 +1384,39 @@ YAMAHA_DEVICE(0x7010, "UB99"),
 	}
 },
 
+{
+	/* Roland SonicCell */
+	USB_DEVICE(0x0582, 0x00c2),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.vendor_name = "Roland",
+		.product_name = "SonicCell",
+		.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 */
 {
 	/*