Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (214 commits)
  ALSA: hda - Add pin-fix for HP dc5750
  ALSA: als4000: Fix potentially invalid DMA mode setup
  ALSA: als4000: enable burst mode
  ALSA: hda - Fix initial capsrc selection in patch_alc269()
  ASoC: TWL4030: Capture route runtime DAPM ordering fix
  ALSA: hda - Add PC-beep whitelist for an Intel board
  ALSA: hda - More relax for pending period handling
  ALSA: hda - Define AC_FMT_* constants
  ALSA: hda - Fix beep frequency on IDT 92HD73xx and 92HD71Bxx codecs
  ALSA: hda - Add support for HDMI HBR passthrough
  ALSA: hda - Set Stream Type in Stream Format according to AES0
  ALSA: hda - Fix Thinkpad X300 so SPDIF is not exposed
  ALSA: hda - FIX to not expose SPDIF on Thinkpad X301, since it does not have the ability to use SPDIF
  ASoC: wm9081: fix resource reclaim in wm9081_register error path
  ASoC: wm8978: fix a memory leak if a wm8978_register fail
  ASoC: wm8974: fix a memory leak if another WM8974 is registered
  ASoC: wm8961: fix resource reclaim in wm8961_register error path
  ASoC: wm8955: fix resource reclaim in wm8955_register error path
  ASoC: wm8940: fix a memory leak if wm8940_register return error
  ASoC: wm8904: fix resource reclaim in wm8904_register error path
  ...
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 71d2863..44f6b19 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -281,19 +281,12 @@
 			no: ACPI OperationRegions are not marked as reserved,
 			no further checks are performed.
 
-	ad1848=		[HW,OSS]
-			Format: <io>,<irq>,<dma>,<dma2>,<type>
-
 	add_efi_memmap	[EFI; X86] Include EFI memory map in
 			kernel's map of available physical RAM.
 
 	advansys=	[HW,SCSI]
 			See header of drivers/scsi/advansys.c.
 
-	aedsp16=	[HW,OSS] Audio Excel DSP 16
-			Format: <io>,<irq>,<dma>,<mss_io>,<mpu_io>,<mpu_irq>
-			See also header of sound/oss/aedsp16.c.
-
 	agp=		[AGP]
 			{ off | try_unsupported }
 			off: disable AGP support
@@ -312,6 +305,9 @@
 	aic79xx=	[HW,SCSI]
 			See Documentation/scsi/aic79xx.txt.
 
+	ALSA		[HW,ALSA]
+			See Documentation/sound/alsa/alsa-parameters.txt
+
 	alignment=	[KNL,ARM]
 			Allow the default userspace alignment fault handler
 			behaviour to be specified.  Bit 0 enables warnings,
@@ -656,8 +652,6 @@
 			Disable PIN 1 of APIC timer
 			Can be useful to work around chipset bugs.
 
-	dmasound=	[HW,OSS] Sound subsystem buffers
-
 	dma_debug=off	If the kernel is compiled with DMA_API_DEBUG support,
 			this option disables the debugging code at boot.
 
@@ -1527,9 +1521,6 @@
 			that the amount of memory usable for all allocations
 			is not too small.
 
-	mpu401=		[HW,OSS]
-			Format: <io>,<irq>
-
 	MTD_Partition=	[MTD]
 			Format: <name>,<region-number>,<size>,<offset>
 
@@ -1854,9 +1845,6 @@
 			For example, to override I2C bus2:
 			omap_mux=i2c2_scl.i2c2_scl=0x100,i2c2_sda.i2c2_sda=0x100
 
-	opl3=		[HW,OSS]
-			Format: <io>
-
 	oprofile.timer=	[HW]
 			Use timer interrupt instead of performance counters
 
@@ -1868,6 +1856,9 @@
 				perfmon on Intel CPUs instead of the
 				CPU specific event set.
 
+	OSS		[HW,OSS]
+			See Documentation/sound/oss/oss-parameters.txt
+
 	osst=		[HW,SCSI] SCSI Tape Driver
 			Format: <buffer_size>,<write_threshold>
 			See also Documentation/scsi/st.txt.
@@ -1904,9 +1895,6 @@
 			Currently this function knows 686a and 8231 chips.
 			Format: [spp|ps2|epp|ecp|ecpepp]
 
-	pas2=		[HW,OSS] Format:
-			<io>,<irq>,<dma>,<dma16>,<sb_io>,<sb_irq>,<sb_dma>,<sb_dma16>
-
 	pas16=		[HW,SCSI]
 			See header of drivers/scsi/pas16.c.
 
@@ -2178,10 +2166,6 @@
 			[HW,MOUSE] Controls Logitech smartscroll autorepeat.
 			0 = disabled, 1 = enabled (default).
 
-	pss=		[HW,OSS] Personal Sound System (ECHO ESC614)
-			Format:
-			<io>,<mss_io>,<mss_irq>,<mss_dma>,<mpu_io>,<mpu_irq>
-
 	pt.		[PARIDE]
 			See Documentation/blockdev/paride.txt.
 
@@ -2397,128 +2381,6 @@
 				1: Fast pin select (default)
 				2: ATC IRMode
 
-	snd-ad1816a=	[HW,ALSA]
-
-	snd-ad1848=	[HW,ALSA]
-
-	snd-ali5451=	[HW,ALSA]
-
-	snd-als100=	[HW,ALSA]
-
-	snd-als4000=	[HW,ALSA]
-
-	snd-azt2320=	[HW,ALSA]
-
-	snd-cmi8330=	[HW,ALSA]
-
-	snd-cmipci=	[HW,ALSA]
-
-	snd-cs4231=	[HW,ALSA]
-
-	snd-cs4232=	[HW,ALSA]
-
-	snd-cs4236=	[HW,ALSA]
-
-	snd-cs4281=	[HW,ALSA]
-
-	snd-cs46xx=	[HW,ALSA]
-
-	snd-dt019x=	[HW,ALSA]
-
-	snd-dummy=	[HW,ALSA]
-
-	snd-emu10k1=	[HW,ALSA]
-
-	snd-ens1370=	[HW,ALSA]
-
-	snd-ens1371=	[HW,ALSA]
-
-	snd-es968=	[HW,ALSA]
-
-	snd-es1688=	[HW,ALSA]
-
-	snd-es18xx=	[HW,ALSA]
-
-	snd-es1938=	[HW,ALSA]
-
-	snd-es1968=	[HW,ALSA]
-
-	snd-fm801=	[HW,ALSA]
-
-	snd-gusclassic=	[HW,ALSA]
-
-	snd-gusextreme=	[HW,ALSA]
-
-	snd-gusmax=	[HW,ALSA]
-
-	snd-hdsp=	[HW,ALSA]
-
-	snd-ice1712=	[HW,ALSA]
-
-	snd-intel8x0=	[HW,ALSA]
-
-	snd-interwave=	[HW,ALSA]
-
-	snd-interwave-stb=
-			[HW,ALSA]
-
-	snd-korg1212=	[HW,ALSA]
-
-	snd-maestro3=	[HW,ALSA]
-
-	snd-mpu401=	[HW,ALSA]
-
-	snd-mtpav=	[HW,ALSA]
-
-	snd-nm256=	[HW,ALSA]
-
-	snd-opl3sa2=	[HW,ALSA]
-
-	snd-opti92x-ad1848=
-			[HW,ALSA]
-
-	snd-opti92x-cs4231=
-			[HW,ALSA]
-
-	snd-opti93x=	[HW,ALSA]
-
-	snd-pmac=	[HW,ALSA]
-
-	snd-rme32=	[HW,ALSA]
-
-	snd-rme96=	[HW,ALSA]
-
-	snd-rme9652=	[HW,ALSA]
-
-	snd-sb8=	[HW,ALSA]
-
-	snd-sb16=	[HW,ALSA]
-
-	snd-sbawe=	[HW,ALSA]
-
-	snd-serial=	[HW,ALSA]
-
-	snd-sgalaxy=	[HW,ALSA]
-
-	snd-sonicvibes=	[HW,ALSA]
-
-	snd-sun-amd7930=
-			[HW,ALSA]
-
-	snd-sun-cs4231=	[HW,ALSA]
-
-	snd-trident=	[HW,ALSA]
-
-	snd-usb-audio=	[HW,ALSA,USB]
-
-	snd-via82xx=	[HW,ALSA]
-
-	snd-virmidi=	[HW,ALSA]
-
-	snd-wavefront=	[HW,ALSA]
-
-	snd-ymfpci=	[HW,ALSA]
-
 	softlockup_panic=
 			[KNL] Should the soft-lockup detector generate panics.
 
@@ -2533,9 +2395,6 @@
 	spia_pedr=
 	spia_peddr=
 
-	sscape=		[HW,OSS]
-			Format: <io>,<irq>,<dma>,<mpu_io>,<mpu_irq>
-
 	st=		[HW,SCSI] SCSI tape parameters (buffers, etc.)
 			See Documentation/scsi/st.txt.
 
@@ -2675,10 +2534,6 @@
 			to facilitate early boot debugging.
 			See also Documentation/trace/events.txt
 
-	trix=		[HW,OSS] MediaTrix AudioTrix Pro
-			Format:
-			<io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq>
-
 	tsc=		Disable clocksource-must-verify flag for TSC.
 			Format: <string>
 			[x86] reliable: mark tsc clocksource as reliable, this
@@ -2695,12 +2550,6 @@
 	u14-34f=	[HW,SCSI] UltraStor 14F/34F SCSI host adapter
 			See header of drivers/scsi/u14-34f.c.
 
-	uart401=	[HW,OSS]
-			Format: <io>,<irq>
-
-	uart6850=	[HW,OSS]
-			Format: <io>,<irq>
-
 	uhash_entries=	[KNL,NET]
 			Set number of hash buckets for UDP/UDP-Lite connections
 
@@ -2866,9 +2715,6 @@
 			overridden by individual drivers. 0 will hide
 			cursors, 1 will display them.
 
-	waveartist=	[HW,OSS]
-			Format: <io>,<irq>,<dma>,<dma2>
-
 	wd33c93=	[HW,SCSI]
 			See header of drivers/scsi/wd33c93.c.
 
@@ -2911,5 +2757,4 @@
 
 TODO:
 
-	Add documentation for ALSA options.
 	Add more DRM drivers.
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
index 1d38b0d..03771d7 100644
--- a/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -114,6 +114,11 @@
   samsung-nc10	Samsung NC10 mini notebook
   auto		auto-config reading BIOS (default)
 
+ALC680
+======
+  base		Base model (ASUS NX90)
+  auto		auto-config reading BIOS (default)
+
 ALC882/883/885/888/889
 ======================
   3stack-dig	3-jack with SPDIF I/O
@@ -282,6 +287,7 @@
   hp		HP Spartan laptop
   hp-dv6736	HP dv6736
   hp-f700	HP Compaq Presario F700
+  ideapad	Lenovo IdeaPad laptop
   lenovo-x200	Lenovo X200 laptop
   toshiba	Toshiba Satellite M300
 
diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt
index 07301de..7fcd1ad 100644
--- a/Documentation/sound/alsa/Procfile.txt
+++ b/Documentation/sound/alsa/Procfile.txt
@@ -103,6 +103,8 @@
 	  bit 2 = Enable additional jiffies check
 	  bit 3 = Log hwptr update at each period interrupt
 	  bit 4 = Log hwptr update at each snd_pcm_update_hw_ptr()
+	  bit 5 = Show last 10 positions on error
+	  bit 6 = Do above only once
 
 	When the bit 0 is set, the driver will show the messages to
 	kernel log when an xrun is detected.  The debug message is
@@ -122,6 +124,12 @@
 	Bits 3 and 4 are for logging the hwptr records.  Note that
 	these will give flood of kernel messages.
 
+	When bit 5 is set, the driver logs the last 10 xrun errors and
+	the proc file shows each jiffies, position, period_size,
+	buffer_size, old_hw_ptr, and hw_ptr_base values.
+
+	When bit 6 is set, the full xrun log is shown only once.
+
 card*/pcm*/sub*/info
 	The general information of this PCM sub-stream.
 
diff --git a/Documentation/sound/alsa/alsa-parameters.txt b/Documentation/sound/alsa/alsa-parameters.txt
new file mode 100644
index 0000000..0fa4067
--- /dev/null
+++ b/Documentation/sound/alsa/alsa-parameters.txt
@@ -0,0 +1,135 @@
+                          ALSA Kernel Parameters
+                          ~~~~~~~~~~~~~~~~~~~~~~
+
+See Documentation/kernel-parameters.txt for general information on
+specifying module parameters.
+
+This document may not be entirely up to date and comprehensive. The command
+"modinfo -p ${modulename}" shows a current list of all parameters of a loadable
+module. Loadable modules, after being loaded into the running kernel, also
+reveal their parameters in /sys/module/${modulename}/parameters/. Some of these
+parameters may be changed at runtime by the command
+"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}".
+
+
+	snd-ad1816a=	[HW,ALSA]
+
+	snd-ad1848=	[HW,ALSA]
+
+	snd-ali5451=	[HW,ALSA]
+
+	snd-als100=	[HW,ALSA]
+
+	snd-als4000=	[HW,ALSA]
+
+	snd-azt2320=	[HW,ALSA]
+
+	snd-cmi8330=	[HW,ALSA]
+
+	snd-cmipci=	[HW,ALSA]
+
+	snd-cs4231=	[HW,ALSA]
+
+	snd-cs4232=	[HW,ALSA]
+
+	snd-cs4236=	[HW,ALSA]
+
+	snd-cs4281=	[HW,ALSA]
+
+	snd-cs46xx=	[HW,ALSA]
+
+	snd-dt019x=	[HW,ALSA]
+
+	snd-dummy=	[HW,ALSA]
+
+	snd-emu10k1=	[HW,ALSA]
+
+	snd-ens1370=	[HW,ALSA]
+
+	snd-ens1371=	[HW,ALSA]
+
+	snd-es968=	[HW,ALSA]
+
+	snd-es1688=	[HW,ALSA]
+
+	snd-es18xx=	[HW,ALSA]
+
+	snd-es1938=	[HW,ALSA]
+
+	snd-es1968=	[HW,ALSA]
+
+	snd-fm801=	[HW,ALSA]
+
+	snd-gusclassic=	[HW,ALSA]
+
+	snd-gusextreme=	[HW,ALSA]
+
+	snd-gusmax=	[HW,ALSA]
+
+	snd-hdsp=	[HW,ALSA]
+
+	snd-ice1712=	[HW,ALSA]
+
+	snd-intel8x0=	[HW,ALSA]
+
+	snd-interwave=	[HW,ALSA]
+
+	snd-interwave-stb=
+			[HW,ALSA]
+
+	snd-korg1212=	[HW,ALSA]
+
+	snd-maestro3=	[HW,ALSA]
+
+	snd-mpu401=	[HW,ALSA]
+
+	snd-mtpav=	[HW,ALSA]
+
+	snd-nm256=	[HW,ALSA]
+
+	snd-opl3sa2=	[HW,ALSA]
+
+	snd-opti92x-ad1848=
+			[HW,ALSA]
+
+	snd-opti92x-cs4231=
+			[HW,ALSA]
+
+	snd-opti93x=	[HW,ALSA]
+
+	snd-pmac=	[HW,ALSA]
+
+	snd-rme32=	[HW,ALSA]
+
+	snd-rme96=	[HW,ALSA]
+
+	snd-rme9652=	[HW,ALSA]
+
+	snd-sb8=	[HW,ALSA]
+
+	snd-sb16=	[HW,ALSA]
+
+	snd-sbawe=	[HW,ALSA]
+
+	snd-serial=	[HW,ALSA]
+
+	snd-sgalaxy=	[HW,ALSA]
+
+	snd-sonicvibes=	[HW,ALSA]
+
+	snd-sun-amd7930=
+			[HW,ALSA]
+
+	snd-sun-cs4231=	[HW,ALSA]
+
+	snd-trident=	[HW,ALSA]
+
+	snd-usb-audio=	[HW,ALSA,USB]
+
+	snd-via82xx=	[HW,ALSA]
+
+	snd-virmidi=	[HW,ALSA]
+
+	snd-wavefront=	[HW,ALSA]
+
+	snd-ymfpci=	[HW,ALSA]
diff --git a/Documentation/sound/oss/oss-parameters.txt b/Documentation/sound/oss/oss-parameters.txt
new file mode 100644
index 0000000..3ab391e
--- /dev/null
+++ b/Documentation/sound/oss/oss-parameters.txt
@@ -0,0 +1,51 @@
+                          OSS Kernel Parameters
+                          ~~~~~~~~~~~~~~~~~~~~~
+
+See Documentation/kernel-parameters.txt for general information on
+specifying module parameters.
+
+This document may not be entirely up to date and comprehensive. The command
+"modinfo -p ${modulename}" shows a current list of all parameters of a loadable
+module. Loadable modules, after being loaded into the running kernel, also
+reveal their parameters in /sys/module/${modulename}/parameters/. Some of these
+parameters may be changed at runtime by the command
+"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}".
+
+
+	ad1848=		[HW,OSS]
+			Format: <io>,<irq>,<dma>,<dma2>,<type>
+
+	aedsp16=	[HW,OSS] Audio Excel DSP 16
+			Format: <io>,<irq>,<dma>,<mss_io>,<mpu_io>,<mpu_irq>
+			See also header of sound/oss/aedsp16.c.
+
+	dmasound=	[HW,OSS] Sound subsystem buffers
+
+	mpu401=		[HW,OSS]
+			Format: <io>,<irq>
+
+	opl3=		[HW,OSS]
+			Format: <io>
+
+	pas2=		[HW,OSS] Format:
+			<io>,<irq>,<dma>,<dma16>,<sb_io>,<sb_irq>,<sb_dma>,<sb_dma16>
+
+	pss=		[HW,OSS] Personal Sound System (ECHO ESC614)
+			Format:
+			<io>,<mss_io>,<mss_irq>,<mss_dma>,<mpu_io>,<mpu_irq>
+
+	sscape=		[HW,OSS]
+			Format: <io>,<irq>,<dma>,<mpu_io>,<mpu_irq>
+
+	trix=		[HW,OSS] MediaTrix AudioTrix Pro
+			Format:
+			<io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq>
+
+	uart401=	[HW,OSS]
+			Format: <io>,<irq>
+
+	uart6850=	[HW,OSS]
+			Format: <io>,<irq>
+
+	waveartist=	[HW,OSS]
+			Format: <io>,<irq>,<dma>,<dma2>
diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c
index 212d970..bc384d3 100644
--- a/arch/arm/mach-davinci/board-da830-evm.c
+++ b/arch/arm/mach-davinci/board-da830-evm.c
@@ -208,7 +208,7 @@
 	.num_serializer = ARRAY_SIZE(da830_iis_serializer_direction),
 	.tdm_slots      = 2,
 	.serial_dir     = da830_iis_serializer_direction,
-	.eventq_no      = EVENTQ_0,
+	.asp_chan_q     = EVENTQ_0,
 	.version	= MCASP_VERSION_2,
 	.txnumevt	= 1,
 	.rxnumevt	= 1,
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index b280efb..e8c8190 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -343,7 +343,7 @@
 	.num_serializer	= ARRAY_SIZE(da850_iis_serializer_direction),
 	.tdm_slots	= 2,
 	.serial_dir	= da850_iis_serializer_direction,
-	.eventq_no	= EVENTQ_1,
+	.asp_chan_q	= EVENTQ_1,
 	.version	= MCASP_VERSION_2,
 	.txnumevt	= 1,
 	.rxnumevt	= 1,
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c
index 6d88893..87521f2 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -323,7 +323,7 @@
 		.num_serializer = ARRAY_SIZE(dm646x_iis_serializer_direction),
 		.tdm_slots      = 2,
 		.serial_dir     = dm646x_iis_serializer_direction,
-		.eventq_no      = EVENTQ_0,
+		.asp_chan_q     = EVENTQ_0,
 	},
 	{
 		.tx_dma_offset  = 0x400,
@@ -332,7 +332,7 @@
 		.num_serializer = ARRAY_SIZE(dm646x_dit_serializer_direction),
 		.tdm_slots      = 32,
 		.serial_dir     = dm646x_dit_serializer_direction,
-		.eventq_no      = EVENTQ_0,
+		.asp_chan_q     = EVENTQ_0,
 	},
 };
 
diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h
index 834725f..9aa2409 100644
--- a/arch/arm/mach-davinci/include/mach/asp.h
+++ b/arch/arm/mach-davinci/include/mach/asp.h
@@ -52,7 +52,8 @@
 struct snd_platform_data {
 	u32 tx_dma_offset;
 	u32 rx_dma_offset;
-	enum dma_event_q eventq_no;	/* event queue number */
+	enum dma_event_q asp_chan_q;	/* event queue number for ASP channel */
+	enum dma_event_q ram_chan_q;	/* event queue number for RAM channel */
 	unsigned int codec_fmt;
 	/*
 	 * Allowing this is more efficient and eliminates left and right swaps
@@ -63,6 +64,49 @@
 	unsigned sram_size_playback;
 	unsigned sram_size_capture;
 
+	/*
+	 * If McBSP peripheral gets the clock from an external pin,
+	 * there are three chooses, that are MCBSP_CLKX, MCBSP_CLKR
+	 * and MCBSP_CLKS.
+	 * Depending on different hardware connections it is possible
+	 * to use this setting to change the behaviour of McBSP
+	 * driver. The dm365_clk_input_pin enum is available for dm365
+	 */
+	int clk_input_pin;
+
+	/*
+	 * This flag works when both clock and FS are outputs for the cpu
+	 * and makes clock more accurate (FS is not symmetrical and the
+	 * clock is very fast.
+	 * The clock becoming faster is named
+	 * i2s continuous serial clock (I2S_SCK) and it is an externally
+	 * visible bit clock.
+	 *
+	 * first line : WordSelect
+	 * second line : ContinuousSerialClock
+	 * third line: SerialData
+	 *
+	 * SYMMETRICAL APPROACH:
+	 *   _______________________          LEFT
+	 * _|         RIGHT         |______________________|
+	 *     _   _         _   _   _   _         _   _
+	 *   _| |_| |_ x16 _| |_| |_| |_| |_ x16 _| |_| |_
+	 *     _   _         _   _   _   _         _   _
+	 *   _/ \_/ \_ ... _/ \_/ \_/ \_/ \_ ... _/ \_/ \_
+	 *    \_/ \_/       \_/ \_/ \_/ \_/       \_/ \_/
+	 *
+	 * ACCURATE CLOCK APPROACH:
+	 *   ______________          LEFT
+	 * _|     RIGHT    |_______________________________|
+	 *     _         _   _         _   _   _   _   _   _
+	 *   _| |_ x16 _| |_| |_ x16 _| |_| |_| |_| |_| |_| |
+	 *     _         _   _          _      dummy cycles
+	 *   _/ \_ ... _/ \_/ \_  ... _/ \__________________
+	 *    \_/       \_/ \_/        \_/
+	 *
+	 */
+	bool i2s_accurate_sck;
+
 	/* McASP specific fields */
 	int tdm_slots;
 	u8 op_mode;
@@ -78,6 +122,11 @@
 	MCASP_VERSION_2,	/* DA8xx/OMAPL1x */
 };
 
+enum dm365_clk_input_pin {
+	MCBSP_CLKR = 0,		/* DM365 */
+	MCBSP_CLKS,
+};
+
 #define INACTIVE_MODE	0
 #define TX_MODE		1
 #define RX_MODE		2
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index 7f303976..8bf3cec 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -43,7 +43,8 @@
 
 static int set_keytchclk_rate(struct clk *clk, unsigned long rate);
 static int set_div_rate(struct clk *clk, unsigned long rate);
-
+static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate);
+static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate);
 
 static struct clk clk_xtali = {
 	.rate		= EP93XX_EXT_CLK_RATE,
@@ -112,6 +113,29 @@
 	.set_rate	= set_div_rate,
 };
 
+static struct clk clk_i2s_mclk = {
+	.sw_locked	= 1,
+	.enable_reg	= EP93XX_SYSCON_I2SCLKDIV,
+	.enable_mask	= EP93XX_SYSCON_CLKDIV_ENABLE,
+	.set_rate	= set_div_rate,
+};
+
+static struct clk clk_i2s_sclk = {
+	.sw_locked	= 1,
+	.parent		= &clk_i2s_mclk,
+	.enable_reg	= EP93XX_SYSCON_I2SCLKDIV,
+	.enable_mask	= EP93XX_SYSCON_I2SCLKDIV_SENA,
+	.set_rate	= set_i2s_sclk_rate,
+};
+
+static struct clk clk_i2s_lrclk = {
+	.sw_locked	= 1,
+	.parent		= &clk_i2s_sclk,
+	.enable_reg	= EP93XX_SYSCON_I2SCLKDIV,
+	.enable_mask	= EP93XX_SYSCON_I2SCLKDIV_SENA,
+	.set_rate	= set_i2s_lrclk_rate,
+};
+
 /* DMA Clocks */
 static struct clk clk_m2p0 = {
 	.parent		= &clk_h,
@@ -191,6 +215,9 @@
 	INIT_CK("ep93xx-keypad",	NULL,		&clk_keypad),
 	INIT_CK("ep93xx-fb",		NULL,		&clk_video),
 	INIT_CK("ep93xx-spi.0",		NULL,		&clk_spi),
+	INIT_CK("ep93xx-i2s",		"mclk",		&clk_i2s_mclk),
+	INIT_CK("ep93xx-i2s",		"sclk",		&clk_i2s_sclk),
+	INIT_CK("ep93xx-i2s",		"lrclk",	&clk_i2s_lrclk),
 	INIT_CK(NULL,			"pwm_clk",	&clk_pwm),
 	INIT_CK(NULL,			"m2p0",		&clk_m2p0),
 	INIT_CK(NULL,			"m2p1",		&clk_m2p1),
@@ -401,6 +428,44 @@
 	return 0;
 }
 
+static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned val = __raw_readl(clk->enable_reg);
+
+	if (rate == clk_i2s_mclk.rate / 2)
+		ep93xx_syscon_swlocked_write(val & ~EP93XX_I2SCLKDIV_SDIV, 
+					     clk->enable_reg);
+	else if (rate == clk_i2s_mclk.rate / 4)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_SDIV, 
+					     clk->enable_reg);
+	else
+		return -EINVAL;
+
+	clk_i2s_sclk.rate = rate;
+	return 0;
+}
+
+static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned val = __raw_readl(clk->enable_reg) & 
+		~EP93XX_I2SCLKDIV_LRDIV_MASK;
+	
+	if (rate == clk_i2s_sclk.rate / 32)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV32,
+					     clk->enable_reg);
+	else if (rate == clk_i2s_sclk.rate / 64)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV64,
+					     clk->enable_reg);
+	else if (rate == clk_i2s_sclk.rate / 128)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV128,
+					     clk->enable_reg);
+	else
+		return -EINVAL;
+
+	clk_i2s_lrclk.rate = rate;
+	return 0;
+}
+
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
 	if (clk->set_rate)
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 8e37a04..4cb55d3 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -758,6 +758,73 @@
 }
 EXPORT_SYMBOL(ep93xx_keypad_release_gpio);
 
+/*************************************************************************
+ * EP93xx I2S audio peripheral handling
+ *************************************************************************/
+static struct resource ep93xx_i2s_resource[] = {
+	{
+		.start	= EP93XX_I2S_PHYS_BASE,
+		.end	= EP93XX_I2S_PHYS_BASE + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device ep93xx_i2s_device = {
+	.name		= "ep93xx-i2s",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(ep93xx_i2s_resource),
+	.resource	= ep93xx_i2s_resource,
+};
+
+void __init ep93xx_register_i2s(void)
+{
+	platform_device_register(&ep93xx_i2s_device);
+}
+
+#define EP93XX_SYSCON_DEVCFG_I2S_MASK	(EP93XX_SYSCON_DEVCFG_I2SONSSP | \
+					 EP93XX_SYSCON_DEVCFG_I2SONAC97)
+
+#define EP93XX_I2SCLKDIV_MASK		(EP93XX_SYSCON_I2SCLKDIV_ORIDE | \
+					 EP93XX_SYSCON_I2SCLKDIV_SPOL)
+
+int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config)
+{
+	unsigned val;
+
+	/* Sanity check */
+	if (i2s_pins & ~EP93XX_SYSCON_DEVCFG_I2S_MASK)
+		return -EINVAL;
+	if (i2s_config & ~EP93XX_I2SCLKDIV_MASK)
+		return -EINVAL;
+
+	/* Must have only one of I2SONSSP/I2SONAC97 set */
+	if ((i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONSSP) ==
+	    (i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONAC97))
+		return -EINVAL;
+
+	ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK);
+	ep93xx_devcfg_set_bits(i2s_pins);
+
+	/*
+	 * This is potentially racy with the clock api for i2s_mclk, sclk and 
+	 * lrclk. Since the i2s driver is the only user of those clocks we
+	 * rely on it to prevent parallel use of this function and the 
+	 * clock api for the i2s clocks.
+	 */
+	val = __raw_readl(EP93XX_SYSCON_I2SCLKDIV);
+	val &= ~EP93XX_I2SCLKDIV_MASK;
+	val |= i2s_config;
+	ep93xx_syscon_swlocked_write(val, EP93XX_SYSCON_I2SCLKDIV);
+
+	return 0;
+}
+EXPORT_SYMBOL(ep93xx_i2s_acquire);
+
+void ep93xx_i2s_release(void)
+{
+	ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK);
+}
+EXPORT_SYMBOL(ep93xx_i2s_release);
 
 extern void ep93xx_gpio_init(void);
 
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
index b1e096f..c54b3e5 100644
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
@@ -93,6 +93,7 @@
 /* APB peripherals */
 #define EP93XX_TIMER_BASE		EP93XX_APB_IOMEM(0x00010000)
 
+#define EP93XX_I2S_PHYS_BASE		EP93XX_APB_PHYS(0x00020000)
 #define EP93XX_I2S_BASE			EP93XX_APB_IOMEM(0x00020000)
 
 #define EP93XX_SECURITY_BASE		EP93XX_APB_IOMEM(0x00030000)
@@ -194,6 +195,15 @@
 #define EP93XX_SYSCON_CLKDIV_ESEL	(1<<14)
 #define EP93XX_SYSCON_CLKDIV_PSEL	(1<<13)
 #define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT	8
+#define EP93XX_SYSCON_I2SCLKDIV		EP93XX_SYSCON_REG(0x8c)
+#define EP93XX_SYSCON_I2SCLKDIV_SENA	(1<<31)
+#define EP93XX_SYSCON_I2SCLKDIV_ORIDE   (1<<29)
+#define EP93XX_SYSCON_I2SCLKDIV_SPOL	(1<<19)
+#define EP93XX_I2SCLKDIV_SDIV		(1 << 16)
+#define EP93XX_I2SCLKDIV_LRDIV32	(0 << 17)
+#define EP93XX_I2SCLKDIV_LRDIV64	(1 << 17)
+#define EP93XX_I2SCLKDIV_LRDIV128 	(2 << 17)
+#define EP93XX_I2SCLKDIV_LRDIV_MASK 	(3 << 17)
 #define EP93XX_SYSCON_KEYTCHCLKDIV	EP93XX_SYSCON_REG(0x90)
 #define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN	(1<<31)
 #define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV	(1<<16)
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index a6c0917..3330b36 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -58,6 +58,9 @@
 void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data);
 int ep93xx_keypad_acquire_gpio(struct platform_device *pdev);
 void ep93xx_keypad_release_gpio(struct platform_device *pdev);
+void ep93xx_register_i2s(void);
+int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config);
+void ep93xx_i2s_release(void);
 
 void ep93xx_init_devices(void);
 extern struct sys_timer ep93xx_timer;
diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c
index 38deaee..a12c893 100644
--- a/arch/arm/mach-ep93xx/snappercl15.c
+++ b/arch/arm/mach-ep93xx/snappercl15.c
@@ -157,6 +157,7 @@
 	ep93xx_register_i2c(&snappercl15_i2c_gpio_data, snappercl15_i2c_data,
 			    ARRAY_SIZE(snappercl15_i2c_data));
 	ep93xx_register_fb(&snappercl15_fb_info);
+	ep93xx_register_i2s();
 	platform_device_register(&snappercl15_nand_device);
 }
 
diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c
index 9dd67c7..1c82d42 100644
--- a/arch/arm/mach-kirkwood/common.c
+++ b/arch/arm/mach-kirkwood/common.c
@@ -25,6 +25,7 @@
 #include <asm/mach/time.h>
 #include <mach/kirkwood.h>
 #include <mach/bridge-regs.h>
+#include <plat/audio.h>
 #include <plat/cache-feroceon-l2.h>
 #include <plat/ehci-orion.h>
 #include <plat/mvsdio.h>
@@ -871,6 +872,42 @@
 	.init = kirkwood_timer_init,
 };
 
+/*****************************************************************************
+ * Audio
+ ****************************************************************************/
+static struct resource kirkwood_i2s_resources[] = {
+	[0] = {
+		.start  = AUDIO_PHYS_BASE,
+		.end    = AUDIO_PHYS_BASE + SZ_16K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = IRQ_KIRKWOOD_I2S,
+		.end    = IRQ_KIRKWOOD_I2S,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct kirkwood_asoc_platform_data kirkwood_i2s_data = {
+	.dram        = &kirkwood_mbus_dram_info,
+	.burst       = 128,
+};
+
+static struct platform_device kirkwood_i2s_device = {
+	.name		= "kirkwood-i2s",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(kirkwood_i2s_resources),
+	.resource	= kirkwood_i2s_resources,
+	.dev		= {
+		.platform_data	= &kirkwood_i2s_data,
+	},
+};
+
+void __init kirkwood_audio_init(void)
+{
+	kirkwood_clk_ctrl |= CGC_AUDIO;
+	platform_device_register(&kirkwood_i2s_device);
+}
 
 /*****************************************************************************
  * General
@@ -939,6 +976,7 @@
 	kirkwood_spi_plat_data.tclk = kirkwood_tclk;
 	kirkwood_uart0_data[0].uartclk = kirkwood_tclk;
 	kirkwood_uart1_data[0].uartclk = kirkwood_tclk;
+	kirkwood_i2s_data.tclk = kirkwood_tclk;
 
 	/*
 	 * Disable propagation of mbus errors to the CPU local bus,
diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h
index 5b2c1c1..95bb0a7 100644
--- a/arch/arm/mach-kirkwood/common.h
+++ b/arch/arm/mach-kirkwood/common.h
@@ -17,6 +17,7 @@
 struct mvsdio_platform_data;
 struct mtd_partition;
 struct mtd_info;
+struct kirkwood_asoc_platform_data;
 
 #define KW_PCIE0	(1 << 0)
 #define KW_PCIE1	(1 << 1)
@@ -46,6 +47,7 @@
 void kirkwood_uart1_init(void);
 void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay);
 void kirkwood_nand_init_rnb(struct mtd_partition *parts, int nr_parts, int (*dev_ready)(struct mtd_info *));
+void kirkwood_audio_init(void);
 
 extern int kirkwood_tclk;
 extern struct sys_timer kirkwood_timer;
diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
index d141af4..93fc2ec 100644
--- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h
+++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
@@ -111,6 +111,9 @@
 
 #define SDIO_PHYS_BASE		(KIRKWOOD_REGS_PHYS_BASE | 0x90000)
 
+#define AUDIO_PHYS_BASE		(KIRKWOOD_REGS_PHYS_BASE | 0xA0000)
+#define AUDIO_VIRT_BASE		(KIRKWOOD_REGS_VIRT_BASE | 0xA0000)
+
 /*
  * Supported devices and revisions.
  */
diff --git a/arch/arm/mach-kirkwood/openrd-setup.c b/arch/arm/mach-kirkwood/openrd-setup.c
index fd64cd2..fd06be6 100644
--- a/arch/arm/mach-kirkwood/openrd-setup.c
+++ b/arch/arm/mach-kirkwood/openrd-setup.c
@@ -15,6 +15,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/ata_platform.h>
 #include <linux/mv643xx_eth.h>
+#include <linux/i2c.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <mach/kirkwood.h>
@@ -60,6 +61,12 @@
 	0
 };
 
+static struct i2c_board_info i2c_board_info[] __initdata = {
+	{
+		I2C_BOARD_INFO("cs42l51", 0x4a),
+	},
+};
+
 static void __init openrd_init(void)
 {
 	/*
@@ -86,6 +93,12 @@
 	kirkwood_sdio_init(&openrd_mvsdio_data);
 
 	kirkwood_i2c_init();
+
+	if (machine_is_openrd_client()) {
+		i2c_register_board_info(0, i2c_board_info,
+			ARRAY_SIZE(i2c_board_info));
+		kirkwood_audio_init();
+	}
 }
 
 static int __init openrd_pci_init(void)
diff --git a/arch/arm/mach-omap2/mcbsp.c b/arch/arm/mach-omap2/mcbsp.c
index c293370..8fb5e53 100644
--- a/arch/arm/mach-omap2/mcbsp.c
+++ b/arch/arm/mach-omap2/mcbsp.c
@@ -133,7 +133,7 @@
 		.rx_irq		= INT_24XX_MCBSP1_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP1_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
-		.buffer_size	= 0x6F,
+		.buffer_size	= 0x80, /* The FIFO has 128 locations */
 	},
 	{
 		.phys_base	= OMAP34XX_MCBSP2_BASE,
@@ -143,7 +143,7 @@
 		.rx_irq		= INT_24XX_MCBSP2_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP2_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
-		.buffer_size	= 0x3FF,
+		.buffer_size	= 0x500, /* The FIFO has 1024 + 256 locations */
 	},
 	{
 		.phys_base	= OMAP34XX_MCBSP3_BASE,
@@ -153,7 +153,7 @@
 		.rx_irq		= INT_24XX_MCBSP3_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP3_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
-		.buffer_size	= 0x6F,
+		.buffer_size	= 0x80, /* The FIFO has 128 locations */
 	},
 	{
 		.phys_base	= OMAP34XX_MCBSP4_BASE,
@@ -162,7 +162,7 @@
 		.rx_irq		= INT_24XX_MCBSP4_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP4_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
-		.buffer_size	= 0x6F,
+		.buffer_size	= 0x80, /* The FIFO has 128 locations */
 	},
 	{
 		.phys_base	= OMAP34XX_MCBSP5_BASE,
@@ -171,7 +171,7 @@
 		.rx_irq		= INT_24XX_MCBSP5_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP5_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
-		.buffer_size	= 0x6F,
+		.buffer_size	= 0x80, /* The FIFO has 128 locations */
 	},
 };
 #define OMAP34XX_MCBSP_PDATA_SZ		ARRAY_SIZE(omap34xx_mcbsp_pdata)
diff --git a/arch/arm/plat-mxc/include/mach/ssi.h b/arch/arm/plat-mxc/include/mach/ssi.h
index c34ded5..63f3c28 100644
--- a/arch/arm/plat-mxc/include/mach/ssi.h
+++ b/arch/arm/plat-mxc/include/mach/ssi.h
@@ -10,6 +10,9 @@
 	unsigned int flags;
 #define IMX_SSI_DMA            (1 << 0)
 #define IMX_SSI_USE_AC97       (1 << 1)
+#define IMX_SSI_NET            (1 << 2)
+#define IMX_SSI_SYN            (1 << 3)
+#define IMX_SSI_USE_I2S_SLAVE  (1 << 4)
 	void (*ac97_reset) (struct snd_ac97 *ac97);
 	void (*ac97_warm_reset)(struct snd_ac97 *ac97);
 };
diff --git a/arch/arm/plat-omap/include/plat/mcbsp.h b/arch/arm/plat-omap/include/plat/mcbsp.h
index 975744f..b4ff6a1 100644
--- a/arch/arm/plat-omap/include/plat/mcbsp.h
+++ b/arch/arm/plat-omap/include/plat/mcbsp.h
@@ -473,6 +473,7 @@
 void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold);
 u16 omap_mcbsp_get_max_tx_threshold(unsigned int id);
 u16 omap_mcbsp_get_max_rx_threshold(unsigned int id);
+u16 omap_mcbsp_get_fifo_size(unsigned int id);
 u16 omap_mcbsp_get_tx_delay(unsigned int id);
 u16 omap_mcbsp_get_rx_delay(unsigned int id);
 int omap_mcbsp_get_dma_op_mode(unsigned int id);
@@ -483,6 +484,7 @@
 { }
 static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; }
 static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; }
+static inline u16 omap_mcbsp_get_fifo_size(unsigned int id) { return 0; }
 static inline u16 omap_mcbsp_get_tx_delay(unsigned int id) { return 0; }
 static inline u16 omap_mcbsp_get_rx_delay(unsigned int id) { return 0; }
 static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; }
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
index 7e669c9..e31496e 100644
--- a/arch/arm/plat-omap/mcbsp.c
+++ b/arch/arm/plat-omap/mcbsp.c
@@ -481,9 +481,9 @@
 EXPORT_SYMBOL(omap_st_is_enabled);
 
 /*
- * omap_mcbsp_set_tx_threshold configures how to deal
- * with transmit threshold. the threshold value and handler can be
- * configure in here.
+ * omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
+ * The threshold parameter is 1 based, and it is converted (threshold - 1)
+ * for the THRSH2 register.
  */
 void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
 {
@@ -498,14 +498,15 @@
 	}
 	mcbsp = id_to_mcbsp_ptr(id);
 
-	MCBSP_WRITE(mcbsp, THRSH2, threshold);
+	if (threshold && threshold <= mcbsp->max_tx_thres)
+		MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
 }
 EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
 
 /*
- * omap_mcbsp_set_rx_threshold configures how to deal
- * with receive threshold. the threshold value and handler can be
- * configure in here.
+ * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
+ * The threshold parameter is 1 based, and it is converted (threshold - 1)
+ * for the THRSH1 register.
  */
 void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
 {
@@ -520,7 +521,8 @@
 	}
 	mcbsp = id_to_mcbsp_ptr(id);
 
-	MCBSP_WRITE(mcbsp, THRSH1, threshold);
+	if (threshold && threshold <= mcbsp->max_rx_thres)
+		MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
 }
 EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
 
@@ -560,8 +562,20 @@
 }
 EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
 
-#define MCBSP2_FIFO_SIZE	0x500 /* 1024 + 256 locations */
-#define MCBSP1345_FIFO_SIZE	0x80  /* 128 locations */
+u16 omap_mcbsp_get_fifo_size(unsigned int id)
+{
+	struct omap_mcbsp *mcbsp;
+
+	if (!omap_mcbsp_check_valid_id(id)) {
+		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+		return -ENODEV;
+	}
+	mcbsp = id_to_mcbsp_ptr(id);
+
+	return mcbsp->pdata->buffer_size;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_fifo_size);
+
 /*
  * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
  */
@@ -580,10 +594,7 @@
 	buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
 
 	/* Number of slots are different in McBSP ports */
-	if (mcbsp->id == 2)
-		return MCBSP2_FIFO_SIZE - buffstat;
-	else
-		return MCBSP1345_FIFO_SIZE - buffstat;
+	return mcbsp->pdata->buffer_size - buffstat;
 }
 EXPORT_SYMBOL(omap_mcbsp_get_tx_delay);
 
@@ -1683,8 +1694,16 @@
 {
 	mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
 	if (cpu_is_omap34xx()) {
-		mcbsp->max_tx_thres = max_thres(mcbsp);
-		mcbsp->max_rx_thres = max_thres(mcbsp);
+		/*
+		 * Initially configure the maximum thresholds to a safe value.
+		 * The McBSP FIFO usage with these values should not go under
+		 * 16 locations.
+		 * If the whole FIFO without safety buffer is used, than there
+		 * is a possibility that the DMA will be not able to push the
+		 * new data on time, causing channel shifts in runtime.
+		 */
+		mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
+		mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
 		/*
 		 * REVISIT: Set dmap_op_mode to THRESHOLD as default
 		 * for mcbsp2 instances.
diff --git a/arch/arm/plat-orion/include/plat/audio.h b/arch/arm/plat-orion/include/plat/audio.h
new file mode 100644
index 0000000..9cf1f78
--- /dev/null
+++ b/arch/arm/plat-orion/include/plat/audio.h
@@ -0,0 +1,11 @@
+#ifndef __PLAT_AUDIO_H
+#define __PLAT_AUDIO_H
+
+#include <linux/mbus.h>
+
+struct kirkwood_asoc_platform_data {
+	u32 tclk;
+	struct mbus_dram_target_info *dram;
+	int burst;
+};
+#endif
diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c
index ae42695..68142df 100644
--- a/arch/um/drivers/hostaudio_kern.c
+++ b/arch/um/drivers/hostaudio_kern.c
@@ -8,6 +8,7 @@
 #include "linux/slab.h"
 #include "linux/sound.h"
 #include "linux/soundcard.h"
+#include "linux/smp_lock.h"
 #include "asm/uaccess.h"
 #include "init.h"
 #include "os.h"
@@ -198,7 +199,10 @@
 	if (file->f_mode & FMODE_WRITE)
 		w = 1;
 
+	lock_kernel();
 	ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0);
+	unlock_kernel();
+
 	if (ret < 0) {
 		kfree(state);
 		return ret;
@@ -254,7 +258,9 @@
 	if (file->f_mode & FMODE_WRITE)
 		w = 1;
 
+	lock_kernel();
 	ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0);
+	unlock_kernel();
 
 	if (ret < 0) {
 		printk(KERN_ERR "hostaudio_open_mixdev failed to open '%s', "
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index b91115f..1f48ceb 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -61,7 +61,7 @@
 #define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
 	+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
 /* B.3.2  Class-Specific AC Interface Descriptor */
-static struct uac_ac_header_descriptor_v1_2 ac_header_desc = {
+static struct uac1_ac_header_descriptor_2 ac_header_desc = {
 	.bLength =		UAC_DT_AC_HEADER_LENGTH,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	UAC_HEADER,
@@ -125,7 +125,7 @@
 };
 
 #define OUTPUT_TERMINAL_ID	3
-static struct uac_output_terminal_descriptor_v1 output_terminal_desc = {
+static struct uac1_output_terminal_descriptor output_terminal_desc = {
 	.bLength		= UAC_DT_OUTPUT_TERMINAL_SIZE,
 	.bDescriptorType	= USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype	= UAC_OUTPUT_TERMINAL,
@@ -155,7 +155,7 @@
 };
 
 /* B.4.2  Class-Specific AS Interface Descriptor */
-static struct uac_as_header_descriptor_v1 as_header_desc = {
+static struct uac1_as_header_descriptor as_header_desc = {
 	.bLength =		UAC_DT_AS_HEADER_SIZE,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	UAC_AS_GENERAL,
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 2b56ce6..b7bf880 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -238,7 +238,7 @@
 };
 
 /* B.3.2  Class-Specific AC Interface Descriptor */
-static const struct uac_ac_header_descriptor_v1_1 ac_header_desc = {
+static const struct uac1_ac_header_descriptor_1 ac_header_desc = {
 	.bLength =		UAC_DT_AC_HEADER_SIZE(1),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	USB_MS_HEADER,
diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
index 383b94b..964cb60 100644
--- a/include/linux/usb/audio-v2.h
+++ b/include/linux/usb/audio-v2.h
@@ -18,6 +18,21 @@
 /* v1.0 and v2.0 of this standard have many things in common. For the rest
  * of the definitions, please refer to audio.h */
 
+/*
+ * bmControl field decoders
+ *
+ * From the USB Audio spec v2.0:
+ *
+ *   bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
+ *   each containing a set of bit pairs. If a Control is present,
+ *   it must be Host readable. If a certain Control is not
+ *   present then the bit pair must be set to 0b00.
+ *   If a Control is present but read-only, the bit pair must be
+ *   set to 0b01. If a Control is also Host programmable, the bit
+ *   pair must be set to 0b11. The value 0b10 is not allowed.
+ *
+ */
+
 static inline bool uac2_control_is_readable(u32 bmControls, u8 control)
 {
 	return (bmControls >> (control * 2)) & 0x1;
@@ -121,7 +136,7 @@
 
 /* 4.9.2 Class-Specific AS Interface Descriptor */
 
-struct uac_as_header_descriptor_v2 {
+struct uac2_as_header_descriptor {
 	__u8 bLength;
 	__u8 bDescriptorType;
 	__u8 bDescriptorSubtype;
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index c51200c..a54b825 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -39,8 +39,8 @@
 #define UAC_MIXER_UNIT			0x04
 #define UAC_SELECTOR_UNIT		0x05
 #define UAC_FEATURE_UNIT		0x06
-#define UAC_PROCESSING_UNIT_V1		0x07
-#define UAC_EXTENSION_UNIT_V1		0x08
+#define UAC1_PROCESSING_UNIT		0x07
+#define UAC1_EXTENSION_UNIT		0x08
 
 /* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
 #define UAC_AS_GENERAL			0x01
@@ -151,7 +151,7 @@
 
 /* Terminal Control Selectors */
 /* 4.3.2  Class-Specific AC Interface Descriptor */
-struct uac_ac_header_descriptor_v1 {
+struct uac1_ac_header_descriptor {
 	__u8  bLength;			/* 8 + n */
 	__u8  bDescriptorType;		/* USB_DT_CS_INTERFACE */
 	__u8  bDescriptorSubtype;	/* UAC_MS_HEADER */
@@ -165,7 +165,7 @@
 
 /* As above, but more useful for defining your own descriptors: */
 #define DECLARE_UAC_AC_HEADER_DESCRIPTOR(n)			\
-struct uac_ac_header_descriptor_v1_##n {			\
+struct uac1_ac_header_descriptor_##n {			\
 	__u8  bLength;						\
 	__u8  bDescriptorType;					\
 	__u8  bDescriptorSubtype;				\
@@ -205,7 +205,7 @@
 #define UAC_TERMINAL_CS_COPY_PROTECT_CONTROL		0x01
 
 /* 4.3.2.2 Output Terminal Descriptor */
-struct uac_output_terminal_descriptor_v1 {
+struct uac1_output_terminal_descriptor {
 	__u8  bLength;			/* in bytes: 9 */
 	__u8  bDescriptorType;		/* CS_INTERFACE descriptor type */
 	__u8  bDescriptorSubtype;	/* OUTPUT_TERMINAL descriptor subtype */
@@ -395,7 +395,7 @@
 }
 
 /* 4.5.2 Class-Specific AS Interface Descriptor */
-struct uac_as_header_descriptor_v1 {
+struct uac1_as_header_descriptor {
 	__u8  bLength;			/* in bytes: 7 */
 	__u8  bDescriptorType;		/* USB_DT_CS_INTERFACE */
 	__u8  bDescriptorSubtype;	/* AS_GENERAL */
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 9f1eecf..a1803ec 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -212,7 +212,11 @@
 #define	SNDRV_PCM_FORMAT_S18_3BE	((__force snd_pcm_format_t) 41)	/* in three bytes */
 #define	SNDRV_PCM_FORMAT_U18_3LE	((__force snd_pcm_format_t) 42)	/* in three bytes */
 #define	SNDRV_PCM_FORMAT_U18_3BE	((__force snd_pcm_format_t) 43)	/* in three bytes */
-#define	SNDRV_PCM_FORMAT_LAST		SNDRV_PCM_FORMAT_U18_3BE
+#define	SNDRV_PCM_FORMAT_G723_24	((__force snd_pcm_format_t) 44) /* 8 samples in 3 bytes */
+#define	SNDRV_PCM_FORMAT_G723_24_1B	((__force snd_pcm_format_t) 45) /* 1 sample in 1 byte */
+#define	SNDRV_PCM_FORMAT_G723_40	((__force snd_pcm_format_t) 46) /* 8 Samples in 5 bytes */
+#define	SNDRV_PCM_FORMAT_G723_40_1B	((__force snd_pcm_format_t) 47) /* 1 sample in 1 byte */
+#define	SNDRV_PCM_FORMAT_LAST		SNDRV_PCM_FORMAT_G723_40_1B
 
 #ifdef SNDRV_LITTLE_ENDIAN
 #define	SNDRV_PCM_FORMAT_S16		SNDRV_PCM_FORMAT_S16_LE
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 6e3a297..85f1c6b 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -174,6 +174,10 @@
 #define SNDRV_PCM_FMTBIT_U18_3LE	(1ULL << SNDRV_PCM_FORMAT_U18_3LE)
 #define SNDRV_PCM_FMTBIT_S18_3BE	(1ULL << SNDRV_PCM_FORMAT_S18_3BE)
 #define SNDRV_PCM_FMTBIT_U18_3BE	(1ULL << SNDRV_PCM_FORMAT_U18_3BE)
+#define SNDRV_PCM_FMTBIT_G723_24	(1ULL << SNDRV_PCM_FORMAT_G723_24)
+#define SNDRV_PCM_FMTBIT_G723_24_1B	(1ULL << SNDRV_PCM_FORMAT_G723_24_1B)
+#define SNDRV_PCM_FMTBIT_G723_40	(1ULL << SNDRV_PCM_FORMAT_G723_40)
+#define SNDRV_PCM_FMTBIT_G723_40_1B	(1ULL << SNDRV_PCM_FORMAT_G723_40_1B)
 
 #ifdef SNDRV_LITTLE_ENDIAN
 #define SNDRV_PCM_FMTBIT_S16		SNDRV_PCM_FMTBIT_S16_LE
@@ -313,7 +317,7 @@
 	struct snd_pcm_mmap_control *control;
 
 	/* -- locking / scheduling -- */
-	unsigned int twake: 1;		/* do transfer (!poll) wakeup */
+	snd_pcm_uframes_t twake; 	/* do transfer (!poll) wakeup if non-zero */
 	wait_queue_head_t sleep;	/* poll sleep */
 	wait_queue_head_t tsleep;	/* transfer sleep */
 	struct fasync_struct *fasync;
diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h
index c022736..9d51d6f3 100644
--- a/include/sound/sh_fsi.h
+++ b/include/sound/sh_fsi.h
@@ -12,6 +12,9 @@
  * published by the Free Software Foundation.
  */
 
+#define FSI_PORT_A	0
+#define FSI_PORT_B	1
+
 /* flags format
 
  * 0xABCDEEFF
@@ -55,12 +58,14 @@
 #define SH_FSI_GET_IFMT(x)	((x >> 8) & SH_FSI_FMT_MASK)
 #define SH_FSI_GET_OFMT(x)	((x >> 0) & SH_FSI_FMT_MASK)
 
-#define SH_FSI_FMT_MONO		(1 << 0)
-#define SH_FSI_FMT_MONO_DELAY	(1 << 1)
-#define SH_FSI_FMT_PCM		(1 << 2)
-#define SH_FSI_FMT_I2S		(1 << 3)
-#define SH_FSI_FMT_TDM		(1 << 4)
-#define SH_FSI_FMT_TDM_DELAY	(1 << 5)
+#define SH_FSI_FMT_MONO		0
+#define SH_FSI_FMT_MONO_DELAY	1
+#define SH_FSI_FMT_PCM		2
+#define SH_FSI_FMT_I2S		3
+#define SH_FSI_FMT_TDM		4
+#define SH_FSI_FMT_TDM_DELAY	5
+#define SH_FSI_FMT_SPDIF	6
+
 
 #define SH_FSI_IFMT_TDM_CH(x) \
 	(SH_FSI_IFMT(TDM)	| SH_FSI_SET_CH_I(x))
@@ -72,9 +77,41 @@
 #define SH_FSI_OFMT_TDM_DELAY_CH(x) \
 	(SH_FSI_OFMT(TDM_DELAY)	| SH_FSI_SET_CH_O(x))
 
+
+/*
+ * set_rate return value
+ *
+ * see ACKMD/BPFMD on
+ *     ACK_MD (FSI2)
+ *     CKG1   (FSI)
+ *
+ * err:  return value < 0
+ *
+ * 0x-00000AB
+ *
+ * A:  ACKMD value
+ * B:  BPFMD value
+ */
+
+#define SH_FSI_ACKMD_MASK	(0xF << 0)
+#define SH_FSI_ACKMD_512	(1 << 0)
+#define SH_FSI_ACKMD_256	(2 << 0)
+#define SH_FSI_ACKMD_128	(3 << 0)
+#define SH_FSI_ACKMD_64		(4 << 0)
+#define SH_FSI_ACKMD_32		(5 << 0)
+
+#define SH_FSI_BPFMD_MASK	(0xF << 4)
+#define SH_FSI_BPFMD_512	(1 << 4)
+#define SH_FSI_BPFMD_256	(2 << 4)
+#define SH_FSI_BPFMD_128	(3 << 4)
+#define SH_FSI_BPFMD_64		(4 << 4)
+#define SH_FSI_BPFMD_32		(5 << 4)
+#define SH_FSI_BPFMD_16		(6 << 4)
+
 struct sh_fsi_platform_info {
 	unsigned long porta_flags;
 	unsigned long portb_flags;
+	int (*set_rate)(int is_porta, int rate); /* for master mode */
 };
 
 extern struct snd_soc_dai fsi_soc_dai[2];
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 66ff4c1..c5d9987 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -273,6 +273,8 @@
 #define SND_SOC_DAPM_POST_PMD	0x8		/* after widget power down */
 #define SND_SOC_DAPM_PRE_REG	0x10	/* before audio path setup */
 #define SND_SOC_DAPM_POST_REG	0x20	/* after audio path setup */
+#define SND_SOC_DAPM_PRE_POST_PMD \
+				(SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)
 
 /* convenience event type detection */
 #define SND_SOC_DAPM_EVENT_ON(e)	\
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 697e7ff..65e9d03 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -170,6 +170,21 @@
 	.get = xhandler_get, .put = xhandler_put, \
 	.private_value = (unsigned long)&xenum }
 
+#define SOC_DOUBLE_R_SX_TLV(xname, xreg_left, xreg_right, xshift,\
+		xmin, xmax, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw_2r_sx, \
+	.get = snd_soc_get_volsw_2r_sx, \
+	.put = snd_soc_put_volsw_2r_sx, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg_left, \
+		 .rreg = xreg_right, .shift = xshift, \
+		 .min = xmin, .max = xmax} }
+
+
 /*
  * Simplified versions of above macros, declaring a struct and calculating
  * ARRAY_SIZE internally
@@ -329,6 +344,12 @@
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_limit_volume(struct snd_soc_codec *codec,
 	const char *name, int max);
+int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 
 /**
  * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
index 3f428d5..6c66496 100644
--- a/include/sound/tlv320dac33-plat.h
+++ b/include/sound/tlv320dac33-plat.h
@@ -15,6 +15,8 @@
 
 struct tlv320dac33_platform_data {
 	int power_gpio;
+	int mode1_latency; /* latency caused by the i2c writes in us */
+	int auto_fifo_config; /* FIFO config based on the period size */
 	int keep_bclk;	/* Keep the BCLK running in FIFO modes */
 	u8 burst_bclkdiv;
 };
diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h
index 509efb0..e475659 100644
--- a/include/sound/uda134x.h
+++ b/include/sound/uda134x.h
@@ -18,6 +18,18 @@
 	struct l3_pins l3;
 	void (*power) (int);
 	int model;
+	/*
+	  ALSA SOC usually puts the device in standby mode when it's not used
+	  for sometime. If you unset is_powered_on_standby the driver will
+	  turn off the ADC/DAC when this callback is invoked and turn it back
+	  on when needed. Unfortunately this will result in a very light bump
+	  (it can be audible only with good earphones). If this bothers you
+	  set is_powered_on_standby, you will have slightly higher power
+	  consumption. Please note that sending the L3 command for ADC is
+	  enough to make the bump, so it doesn't make difference if you
+	  completely take off power from the codec.
+	*/
+	int is_powered_on_standby;
 #define UDA134X_UDA1340 1
 #define UDA134X_UDA1341 2
 #define UDA134X_UDA1344 3
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index e9d98be..e23e0e7 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -67,6 +67,8 @@
 	} else {
 		if (new_hw_ptr == ULONG_MAX) {	/* initialization */
 			snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
+			if (avail > runtime->buffer_size)
+				avail = runtime->buffer_size;
 			runtime->silence_filled = avail > 0 ? avail : 0;
 			runtime->silence_start = (runtime->status->hw_ptr +
 						  runtime->silence_filled) %
@@ -287,8 +289,11 @@
 			return -EPIPE;
 		}
 	}
-	if (avail >= runtime->control->avail_min)
-		wake_up(runtime->twake ? &runtime->tsleep : &runtime->sleep);
+	if (runtime->twake) {
+		if (avail >= runtime->twake)
+			wake_up(&runtime->tsleep);
+	} else if (avail >= runtime->control->avail_min)
+		wake_up(&runtime->sleep);
 	return 0;
 }
 
@@ -1707,7 +1712,7 @@
  * The available space is stored on availp.  When err = 0 and avail = 0
  * on the capture stream, it indicates the stream is in DRAINING state.
  */
-static int wait_for_avail_min(struct snd_pcm_substream *substream,
+static int wait_for_avail(struct snd_pcm_substream *substream,
 			      snd_pcm_uframes_t *availp)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1757,7 +1762,7 @@
 			avail = snd_pcm_playback_avail(runtime);
 		else
 			avail = snd_pcm_capture_avail(runtime);
-		if (avail >= runtime->control->avail_min)
+		if (avail >= runtime->twake)
 			break;
 	}
  _endloop:
@@ -1820,7 +1825,7 @@
 		goto _end_unlock;
 	}
 
-	runtime->twake = 1;
+	runtime->twake = runtime->control->avail_min ? : 1;
 	while (size > 0) {
 		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
 		snd_pcm_uframes_t avail;
@@ -1833,7 +1838,9 @@
 				err = -EAGAIN;
 				goto _end_unlock;
 			}
-			err = wait_for_avail_min(substream, &avail);
+			runtime->twake = min_t(snd_pcm_uframes_t, size,
+					runtime->control->avail_min ? : 1);
+			err = wait_for_avail(substream, &avail);
 			if (err < 0)
 				goto _end_unlock;
 		}
@@ -2042,7 +2049,7 @@
 		goto _end_unlock;
 	}
 
-	runtime->twake = 1;
+	runtime->twake = runtime->control->avail_min ? : 1;
 	while (size > 0) {
 		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
 		snd_pcm_uframes_t avail;
@@ -2060,7 +2067,9 @@
 				err = -EAGAIN;
 				goto _end_unlock;
 			}
-			err = wait_for_avail_min(substream, &avail);
+			runtime->twake = min_t(snd_pcm_uframes_t, size,
+					runtime->control->avail_min ? : 1);
+			err = wait_for_avail(substream, &avail);
 			if (err < 0)
 				goto _end_unlock;
 			if (!avail)
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index ea2bf82..434af3c 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -128,6 +128,14 @@
 		.width = 4, .phys = 4, .le = -1, .signd = -1,
 		.silence = {},
 	},
+	[SNDRV_PCM_FORMAT_G723_24] = {
+		.width = 3, .phys = 3, .le = -1, .signd = -1,
+		.silence = {},
+	},
+	[SNDRV_PCM_FORMAT_G723_40] = {
+		.width = 5, .phys = 5, .le = -1, .signd = -1,
+		.silence = {},
+	},
 	/* FIXME: the following three formats are not defined properly yet */
 	[SNDRV_PCM_FORMAT_MPEG] = {
 		.le = -1, .signd = -1,
@@ -186,6 +194,14 @@
 		.width = 18, .phys = 24, .le = 0, .signd = 0,
 		.silence = { 0x02, 0x00, 0x00 },
 	},
+	[SNDRV_PCM_FORMAT_G723_24_1B] = {
+		.width = 3, .phys = 8, .le = -1, .signd = -1,
+		.silence = {},
+	},
+	[SNDRV_PCM_FORMAT_G723_40_1B] = {
+		.width = 5, .phys = 8, .le = -1, .signd = -1,
+		.silence = {},
+	},
 };
 
 
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index c290cee..480c386 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -170,9 +170,25 @@
 	  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.
+	  The mode is activated by passing 'power_save=X' to the
+	  snd-ac97-codec driver module, where 'X' is the time-out
+	  value, a nonnegative integer that specifies how many
+	  seconds of idle time the driver must count before it may
+	  put the AC97 into power-save mode;  a value of 0 (zero)
+	  disables the use of this power-save mode.
+
+	  After the snd-ac97-codec driver module has been loaded,
+	  the 'power_save' parameter can be set via sysfs as follows:
+
+	    echo 10 > /sys/module/snd_ac97_codec/parameters/power_save
+
+	  In this case, the time-out is set to 10 seconds; setting
+	  the time-out to 1 second (the minimum activation value)
+	  isn't recommended because many applications try to reopen
+	  the device frequently.  A value of 10 seconds would be a
+	  good choice for normal operations.
+
+	  See Documentation/sound/alsa/powersave.txt for more details.
 
 config SND_AC97_POWER_SAVE_DEFAULT
 	int "Default time-out for AC97 power-save mode"
@@ -182,4 +198,6 @@
 	  The default time-out value in seconds for AC97 automatic
 	  power-save mode.  0 means to disable the power-save mode.
 
+	  See SND_AC97_POWER_SAVE for more details.
+
 endif	# SND_DRIVERS
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index 60b6abd..5f3e684 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -549,7 +549,10 @@
 		printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", chip->irq);
 		return err;
 	}
-	request_region(chip->io, DSP_NUMIO, card->shortname);
+	if (request_region(chip->io, DSP_NUMIO, card->shortname) == NULL) {
+		free_irq(chip->irq, chip);
+		return -EBUSY;
+	}
 
 	if (!request_mem_region(chip->base, BUFFSIZE, card->shortname)) {
 		printk(KERN_ERR LOGNAME
diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c
index ccedbfe..2f85c66 100644
--- a/sound/isa/sb/emu8000_pcm.c
+++ b/sound/isa/sb/emu8000_pcm.c
@@ -433,7 +433,8 @@
 	while (count > 0) {
 		unsigned short sval;
 		CHECK_SCHEDULER();
-		get_user(sval, buf);
+		if (get_user(sval, buf))
+			return -EFAULT;
 		EMU8000_SMLD_WRITE(emu, sval);
 		buf++;
 		count--;
@@ -525,12 +526,14 @@
 	while (count-- > 0) {
 		unsigned short sval;
 		CHECK_SCHEDULER();
-		get_user(sval, buf);
+		if (get_user(sval, buf))
+			return -EFAULT;
 		EMU8000_SMLD_WRITE(emu, sval);
 		buf++;
 		if (rec->voices > 1) {
 			CHECK_SCHEDULER();
-			get_user(sval, buf);
+			if (get_user(sval, buf))
+				return -EFAULT;
 			EMU8000_SMRD_WRITE(emu, sval);
 			buf++;
 		}
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
index c1070e3..c4a4cdc 100644
--- a/sound/oss/au1550_ac97.c
+++ b/sound/oss/au1550_ac97.c
@@ -43,6 +43,7 @@
 #include <linux/sound.h>
 #include <linux/slab.h>
 #include <linux/soundcard.h>
+#include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -162,19 +163,10 @@
 static void
 au1550_delay(int msec)
 {
-	unsigned long   tmo;
-	signed long     tmo2;
-
 	if (in_interrupt())
 		return;
 
-	tmo = jiffies + (msec * HZ) / 1000;
-	for (;;) {
-		tmo2 = tmo - jiffies;
-		if (tmo2 <= 0)
-			break;
-		schedule_timeout(tmo2);
-	}
+	schedule_timeout_uninterruptible(msecs_to_jiffies(msec));
 }
 
 static u16
@@ -807,7 +799,9 @@
 static int
 au1550_open_mixdev(struct inode *inode, struct file *file)
 {
+	lock_kernel();
 	file->private_data = &au1550_state;
+	unlock_kernel();
 	return 0;
 }
 
@@ -824,22 +818,26 @@
 	return codec->mixer_ioctl(codec, cmd, arg);
 }
 
-static int
-au1550_ioctl_mixdev(struct inode *inode, struct file *file,
-			       unsigned int cmd, unsigned long arg)
+static long
+au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct au1550_state *s = (struct au1550_state *)file->private_data;
 	struct ac97_codec *codec = s->codec;
+	int ret;
 
-	return mixdev_ioctl(codec, cmd, arg);
+	lock_kernel();
+	ret = mixdev_ioctl(codec, cmd, arg);
+	unlock_kernel();
+
+	return ret;
 }
 
 static /*const */ struct file_operations au1550_mixer_fops = {
-	owner:THIS_MODULE,
-	llseek:au1550_llseek,
-	ioctl:au1550_ioctl_mixdev,
-	open:au1550_open_mixdev,
-	release:au1550_release_mixdev,
+	.owner		= THIS_MODULE,
+	.llseek		= au1550_llseek,
+	.unlocked_ioctl	= au1550_ioctl_mixdev,
+	.open		= au1550_open_mixdev,
+	.release	= au1550_release_mixdev,
 };
 
 static int
@@ -1343,8 +1341,7 @@
 
 
 static int
-au1550_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-							unsigned long arg)
+au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct au1550_state *s = (struct au1550_state *)file->private_data;
 	unsigned long   flags;
@@ -1780,6 +1777,17 @@
 	return mixdev_ioctl(s->codec, cmd, arg);
 }
 
+static long
+au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int ret;
+
+	lock_kernel();
+	ret = au1550_ioctl(file, cmd, arg);
+	unlock_kernel();
+
+	return ret;
+}
 
 static int
 au1550_open(struct inode *inode, struct file *file)
@@ -1797,21 +1805,22 @@
 #endif
 
 	file->private_data = s;
+	lock_kernel();
 	/* wait for device to become free */
 	mutex_lock(&s->open_mutex);
 	while (s->open_mode & file->f_mode) {
-		if (file->f_flags & O_NONBLOCK) {
-			mutex_unlock(&s->open_mutex);
-			return -EBUSY;
-		}
+		ret = -EBUSY;
+		if (file->f_flags & O_NONBLOCK)
+			goto out;
 		add_wait_queue(&s->open_wait, &wait);
 		__set_current_state(TASK_INTERRUPTIBLE);
 		mutex_unlock(&s->open_mutex);
 		schedule();
 		remove_wait_queue(&s->open_wait, &wait);
 		set_current_state(TASK_RUNNING);
+		ret = -ERESTARTSYS;
 		if (signal_pending(current))
-			return -ERESTARTSYS;
+			goto out2;
 		mutex_lock(&s->open_mutex);
 	}
 
@@ -1840,17 +1849,21 @@
 
 	if (file->f_mode & FMODE_READ) {
 		if ((ret = prog_dmabuf_adc(s)))
-			return ret;
+			goto out;
 	}
 	if (file->f_mode & FMODE_WRITE) {
 		if ((ret = prog_dmabuf_dac(s)))
-			return ret;
+			goto out;
 	}
 
 	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
-	mutex_unlock(&s->open_mutex);
 	mutex_init(&s->sem);
-	return 0;
+	ret = 0;
+out:
+	mutex_unlock(&s->open_mutex);
+out2:
+	unlock_kernel();
+	return ret;
 }
 
 static int
@@ -1885,15 +1898,15 @@
 }
 
 static /*const */ struct file_operations au1550_audio_fops = {
-	owner:		THIS_MODULE,
-	llseek:		au1550_llseek,
-	read:		au1550_read,
-	write:		au1550_write,
-	poll:		au1550_poll,
-	ioctl:		au1550_ioctl,
-	mmap:		au1550_mmap,
-	open:		au1550_open,
-	release:	au1550_release,
+	.owner		= THIS_MODULE,
+	.llseek		= au1550_llseek,
+	.read		= au1550_read,
+	.write		= au1550_write,
+	.poll		= au1550_poll,
+	.unlocked_ioctl	= au1550_unlocked_ioctl,
+	.mmap		= au1550_mmap,
+	.open		= au1550_open,
+	.release	= au1550_release,
 };
 
 MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 3f3c3f7..6ecd41a 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -323,9 +323,13 @@
 
 static int mixer_open(struct inode *inode, struct file *file)
 {
-	if (!try_module_get(dmasound.mach.owner))
+	lock_kernel();
+	if (!try_module_get(dmasound.mach.owner)) {
+		unlock_kernel();
 		return -ENODEV;
+	}
 	mixer.busy = 1;
+	unlock_kernel();
 	return 0;
 }
 
@@ -337,8 +341,8 @@
 	unlock_kernel();
 	return 0;
 }
-static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		       u_long arg)
+
+static int mixer_ioctl(struct file *file, u_int cmd, u_long arg)
 {
 	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
 	    mixer.modify_counter++;
@@ -362,11 +366,22 @@
 	return -EINVAL;
 }
 
+static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	lock_kernel();
+	ret = mixer_ioctl(file, cmd, arg);
+	unlock_kernel();
+
+	return ret;
+}
+
 static const struct file_operations mixer_fops =
 {
 	.owner		= THIS_MODULE,
 	.llseek		= no_llseek,
-	.ioctl		= mixer_ioctl,
+	.unlocked_ioctl	= mixer_unlocked_ioctl,
 	.open		= mixer_open,
 	.release	= mixer_release,
 };
@@ -737,8 +752,11 @@
 {
 	int rc;
 
-	if (!try_module_get(dmasound.mach.owner))
+	lock_kernel();
+	if (!try_module_get(dmasound.mach.owner)) {
+		unlock_kernel();
 		return -ENODEV;
+	}
 
 	rc = write_sq_open(file); /* checks the f_mode */
 	if (rc)
@@ -781,10 +799,11 @@
 		sound_set_format(AFMT_MU_LAW);
 	}
 #endif
-
+	unlock_kernel();
 	return 0;
  out:
 	module_put(dmasound.mach.owner);
+	unlock_kernel();
 	return rc;
 }
 
@@ -955,8 +974,7 @@
 	return 0 ;
 }
 
-static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
-		    u_long arg)
+static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
 {
 	int val, result;
 	u_long fmt;
@@ -1114,18 +1132,29 @@
 		return IOCTL_OUT(arg,val);
 
 	default:
-		return mixer_ioctl(inode, file, cmd, arg);
+		return mixer_ioctl(file, cmd, arg);
 	}
 	return -EINVAL;
 }
 
+static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	lock_kernel();
+	ret = sq_ioctl(file, cmd, arg);
+	unlock_kernel();
+
+	return ret;
+}
+
 static const struct file_operations sq_fops =
 {
 	.owner		= THIS_MODULE,
 	.llseek		= no_llseek,
 	.write		= sq_write,
 	.poll		= sq_poll,
-	.ioctl		= sq_ioctl,
+	.unlocked_ioctl	= sq_unlocked_ioctl,
 	.open		= sq_open,
 	.release	= sq_release,
 };
@@ -1226,12 +1255,17 @@
 {
 	char *buffer = state.buf;
 	int len = 0;
+	int ret;
 
+	lock_kernel();
+	ret = -EBUSY;
 	if (state.busy)
-		return -EBUSY;
+		goto out;
 
+	ret = -ENODEV;
 	if (!try_module_get(dmasound.mach.owner))
-		return -ENODEV;
+		goto out;
+
 	state.ptr = 0;
 	state.busy = 1;
 
@@ -1293,7 +1327,10 @@
 		printk(KERN_ERR "dmasound_core: stat buffer overflowed!\n");
 
 	state.len = len;
-	return 0;
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
 }
 
 static int state_release(struct inode *inode, struct file *file)
diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c
index 3bc7104..3c09374 100644
--- a/sound/oss/midi_synth.c
+++ b/sound/oss/midi_synth.c
@@ -523,7 +523,9 @@
 	{
 		unsigned char   data;
 
-		get_user(*(unsigned char *) &data, (unsigned char __user *) &((addr)[hdr_size + i]));
+		if (get_user(data,
+		    (unsigned char __user *)(addr + hdr_size + i)))
+			return -EFAULT;
 
 		eox_seen = (i > 0 && data & 0x80);	/* End of sysex */
 
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index a1e3f96..2e48b17 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -639,21 +639,26 @@
 	return -EINVAL;
 }
 
-static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	int minor = iminor(inode);
+	int minor = iminor(file->f_path.dentry->d_inode);
+	int ret;
 
 	if (cmd == OSS_GETVERSION) {
 		int sound_version = SOUND_VERSION;
 		return put_user(sound_version, (int __user *)arg);
 	}
 
-	if (minor == dev.dsp_minor)
-		return dsp_ioctl(file, cmd, arg);
-	else if (minor == dev.mixer_minor)
-		return mixer_ioctl(cmd, arg);
+	ret = -EINVAL;
 
-	return -EINVAL;
+	lock_kernel();
+	if (minor == dev.dsp_minor)
+		ret = dsp_ioctl(file, cmd, arg);
+	else if (minor == dev.mixer_minor)
+		ret = mixer_ioctl(cmd, arg);
+	unlock_kernel();
+
+	return ret;
 }
 
 static void dsp_write_flush(void)
@@ -756,12 +761,15 @@
 	int minor = iminor(inode);
 	int err = 0;
 
+	lock_kernel();
 	if (minor == dev.dsp_minor) {
 		if ((file->f_mode & FMODE_WRITE &&
 		     test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
 		    (file->f_mode & FMODE_READ &&
-		     test_bit(F_AUDIO_READ_INUSE, &dev.flags)))
-			return -EBUSY;
+		     test_bit(F_AUDIO_READ_INUSE, &dev.flags))) {
+			err = -EBUSY;
+			goto out;
+		}
 
 		if ((err = dsp_open(file)) >= 0) {
 			dev.nresets = 0;
@@ -782,7 +790,8 @@
 		/* nothing */
 	} else
 		err = -EINVAL;
-
+out:
+	unlock_kernel();
 	return err;
 }
 
@@ -1105,7 +1114,7 @@
 	.owner		= THIS_MODULE,
 	.read		= dev_read,
 	.write		= dev_write,
-	.ioctl		= dev_ioctl,
+	.unlocked_ioctl	= dev_ioctl,
 	.open		= dev_open,
 	.release	= dev_release,
 };
@@ -1391,9 +1400,13 @@
 		printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq);
 		return err;
 	}
-	request_region(dev.io, dev.numio, dev.name);
+	if (request_region(dev.io, dev.numio, dev.name) == NULL) {
+		free_irq(dev.irq, &dev);
+		return -EBUSY;
+	}
 
-        if ((err = dsp_full_reset()) < 0) {
+	err = dsp_full_reset();
+	if (err < 0) {
 		release_region(dev.io, dev.numio);
 		free_irq(dev.irq, &dev);
 		return err;
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c
index 4153752..fdb58eb 100644
--- a/sound/oss/sh_dac_audio.c
+++ b/sound/oss/sh_dac_audio.c
@@ -15,7 +15,9 @@
 #include <linux/linkage.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/smp_lock.h>
 #include <linux/sound.h>
+#include <linux/smp_lock.h>
 #include <linux/soundcard.h>
 #include <linux/interrupt.h>
 #include <linux/hrtimer.h>
@@ -92,7 +94,7 @@
 	wakeups_per_second = ktime_set(0, 1000000000 / rate);
 }
 
-static int dac_audio_ioctl(struct inode *inode, struct file *file,
+static int dac_audio_ioctl(struct file *file,
 			   unsigned int cmd, unsigned long arg)
 {
 	int val;
@@ -158,6 +160,17 @@
 	return -EINVAL;
 }
 
+static long dac_audio_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	lock_kernel();
+	ret = dac_audio_ioctl(file, cmd, arg);
+	unlock_kernel();
+
+	return ret;
+}
+
 static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
 			       loff_t * ppos)
 {
@@ -216,13 +229,17 @@
 {
 	if (file->f_mode & FMODE_READ)
 		return -ENODEV;
-	if (in_use)
+
+	lock_kernel();
+	if (in_use) {
+		unlock_kernel();
 		return -EBUSY;
+	}
 
 	in_use = 1;
 
 	dac_audio_start();
-
+	unlock_kernel();
 	return 0;
 }
 
@@ -237,8 +254,8 @@
 
 const struct file_operations dac_audio_fops = {
       .read =		dac_audio_read,
-      .write =	dac_audio_write,
-      .ioctl =	dac_audio_ioctl,
+      .write =		dac_audio_write,
+      .unlocked_ioctl =	dac_audio_unlocked_ioctl,
       .open =		dac_audio_open,
       .release =	dac_audio_release,
 };
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
index 2d9c513..92aa762 100644
--- a/sound/oss/soundcard.c
+++ b/sound/oss/soundcard.c
@@ -210,42 +210,44 @@
 		printk(KERN_ERR "Invalid minor device %d\n", dev);
 		return -ENXIO;
 	}
+	lock_kernel();
 	switch (dev & 0x0f) {
 	case SND_DEV_CTL:
 		dev >>= 4;
 		if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
 			request_module("mixer%d", dev);
 		}
+		retval = -ENXIO;
 		if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
-			return -ENXIO;
+			break;
 	
 		if (!try_module_get(mixer_devs[dev]->owner))
-			return -ENXIO;
+			break;
+
+		retval = 0;
 		break;
 
 	case SND_DEV_SEQ:
 	case SND_DEV_SEQ2:
-		if ((retval = sequencer_open(dev, file)) < 0)
-			return retval;
+		retval = sequencer_open(dev, file);
 		break;
 
 	case SND_DEV_MIDIN:
-		if ((retval = MIDIbuf_open(dev, file)) < 0)
-			return retval;
+		retval = MIDIbuf_open(dev, file);
 		break;
 
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		if ((retval = audio_open(dev, file)) < 0)
-			return retval;
+		retval = audio_open(dev, file);
 		break;
 
 	default:
 		printk(KERN_ERR "Invalid minor device %d\n", dev);
-		return -ENXIO;
+		retval = -ENXIO;
 	}
 
+	unlock_kernel();
 	return 0;
 }
 
diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c
index 3136c88..b15840a 100644
--- a/sound/oss/swarm_cs4297a.c
+++ b/sound/oss/swarm_cs4297a.c
@@ -68,6 +68,7 @@
 #include <linux/delay.h>
 #include <linux/sound.h>
 #include <linux/slab.h>
+#include <linux/smp_lock.h>
 #include <linux/soundcard.h>
 #include <linux/ac97_codec.h>
 #include <linux/pci.h>
@@ -1534,6 +1535,7 @@
 	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
 		  printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n"));
 
+	lock_kernel();
 	list_for_each(entry, &cs4297a_devs)
 	{
 		s = list_entry(entry, struct cs4297a_state, list);
@@ -1544,6 +1546,8 @@
 	{
 		CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
 			printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n"));
+
+		unlock_kernel();
 		return -ENODEV;
 	}
 	VALIDATE_STATE(s);
@@ -1551,6 +1555,7 @@
 
 	CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
 		  printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n"));
+	unlock_kernel();
 
 	return nonseekable_open(inode, file);
 }
@@ -1566,11 +1571,15 @@
 }
 
 
-static int cs4297a_ioctl_mixdev(struct inode *inode, struct file *file,
+static int cs4297a_ioctl_mixdev(struct file *file,
 			       unsigned int cmd, unsigned long arg)
 {
-	return mixer_ioctl((struct cs4297a_state *) file->private_data, cmd,
+	int ret;
+	lock_kernel();
+	ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd,
 			   arg);
+	unlock_kernel();
+	return ret;
 }
 
 
@@ -1580,7 +1589,7 @@
 static const struct file_operations cs4297a_mixer_fops = {
 	.owner		= THIS_MODULE,
 	.llseek		= no_llseek,
-	.ioctl		= cs4297a_ioctl_mixdev,
+	.unlocked_ioctl	= cs4297a_ioctl_mixdev,
 	.open		= cs4297a_open_mixdev,
 	.release	= cs4297a_release_mixdev,
 };
@@ -1944,7 +1953,7 @@
 }
 
 
-static int cs4297a_ioctl(struct inode *inode, struct file *file,
+static int cs4297a_ioctl(struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
 	struct cs4297a_state *s =
@@ -2337,6 +2346,16 @@
 	return mixer_ioctl(s, cmd, arg);
 }
 
+static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
+{
+	int ret;
+
+	lock_kernel();
+	ret = cs4297a_ioctl(file, cmd, arg);
+	unlock_kernel();
+
+	return ret;
+}
 
 static int cs4297a_release(struct inode *inode, struct file *file)
 {
@@ -2369,7 +2388,7 @@
 	return 0;
 }
 
-static int cs4297a_open(struct inode *inode, struct file *file)
+static int cs4297a_locked_open(struct inode *inode, struct file *file)
 {
 	int minor = iminor(inode);
 	struct cs4297a_state *s=NULL;
@@ -2486,6 +2505,16 @@
 	return nonseekable_open(inode, file);
 }
 
+static int cs4297a_open(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	lock_kernel();
+	ret = cs4297a_open(inode, file);
+	unlock_kernel();
+
+	return ret;
+}
 
 // ******************************************************************************************
 //   Wave (audio) file operations struct.
@@ -2496,7 +2525,7 @@
 	.read		= cs4297a_read,
 	.write		= cs4297a_write,
 	.poll		= cs4297a_poll,
-	.ioctl		= cs4297a_ioctl,
+	.unlocked_ioctl	= cs4297a_unlocked_ioctl,
 	.mmap		= cs4297a_mmap,
 	.open		= cs4297a_open,
 	.release	= cs4297a_release,
diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c
index ac39a53..f0e0caa 100644
--- a/sound/oss/vidc.c
+++ b/sound/oss/vidc.c
@@ -491,9 +491,6 @@
 	vidc_adev = adev;
 	vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
 
-#if defined(CONFIG_SOUND_SOFTOSS) || defined(CONFIG_SOUND_SOFTOSS_MODULE)
-	softoss_dev = adev;
-#endif
 	return;
 
 irq_failed:
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
index 20b3b32..8cd73cd 100644
--- a/sound/oss/vwsnd.c
+++ b/sound/oss/vwsnd.c
@@ -2429,8 +2429,7 @@
 	return mask;
 }
 
-static int vwsnd_audio_do_ioctl(struct inode *inode,
-				struct file *file,
+static int vwsnd_audio_do_ioctl(struct file *file,
 				unsigned int cmd,
 				unsigned long arg)
 {
@@ -2446,8 +2445,8 @@
 	int ival;
 
 	
-	DBGEV("(inode=0x%p, file=0x%p, cmd=0x%x, arg=0x%lx)\n",
-	      inode, file, cmd, arg);
+	DBGEV("(file=0x%p, cmd=0x%x, arg=0x%lx)\n",
+	      file, cmd, arg);
 	switch (cmd) {
 	case OSS_GETVERSION:		/* _SIOR ('M', 118, int) */
 		DBGX("OSS_GETVERSION\n");
@@ -2885,17 +2884,19 @@
 	return -EINVAL;
 }
 
-static int vwsnd_audio_ioctl(struct inode *inode,
-				struct file *file,
+static long vwsnd_audio_ioctl(struct file *file,
 				unsigned int cmd,
 				unsigned long arg)
 {
 	vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
 	int ret;
 
+	lock_kernel();
 	mutex_lock(&devc->io_mutex);
-	ret = vwsnd_audio_do_ioctl(inode, file, cmd, arg);
+	ret = vwsnd_audio_do_ioctl(file, cmd, arg);
 	mutex_unlock(&devc->io_mutex);
+	unlock_kernel();
+
 	return ret;
 }
 
@@ -2921,6 +2922,7 @@
 
 	DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
 
+	lock_kernel();
 	INC_USE_COUNT;
 	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
 		if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
@@ -2928,6 +2930,7 @@
 
 	if (devc == NULL) {
 		DEC_USE_COUNT;
+		unlock_kernel();
 		return -ENODEV;
 	}
 
@@ -2936,11 +2939,13 @@
 		mutex_unlock(&devc->open_mutex);
 		if (file->f_flags & O_NONBLOCK) {
 			DEC_USE_COUNT;
+			unlock_kernel();
 			return -EBUSY;
 		}
 		interruptible_sleep_on(&devc->open_wait);
 		if (signal_pending(current)) {
 			DEC_USE_COUNT;
+			unlock_kernel();
 			return -ERESTARTSYS;
 		}
 		mutex_lock(&devc->open_mutex);
@@ -2993,6 +2998,7 @@
 
 	file->private_data = devc;
 	DBGRV();
+	unlock_kernel();
 	return 0;
 }
 
@@ -3044,7 +3050,7 @@
 	.read =		vwsnd_audio_read,
 	.write =	vwsnd_audio_write,
 	.poll =		vwsnd_audio_poll,
-	.ioctl =	vwsnd_audio_ioctl,
+	.unlocked_ioctl = vwsnd_audio_ioctl,
 	.mmap =		vwsnd_audio_mmap,
 	.open =		vwsnd_audio_open,
 	.release =	vwsnd_audio_release,
@@ -3062,15 +3068,18 @@
 	DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
 
 	INC_USE_COUNT;
+	lock_kernel();
 	for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
 		if (devc->mixer_minor == iminor(inode))
 			break;
 
 	if (devc == NULL) {
 		DEC_USE_COUNT;
+		unlock_kernel();
 		return -ENODEV;
 	}
 	file->private_data = devc;
+	unlock_kernel();
 	return 0;
 }
 
@@ -3203,8 +3212,7 @@
 
 /* This is the ioctl entry to the mixer driver. */
 
-static int vwsnd_mixer_ioctl(struct inode *ioctl,
-			      struct file *file,
+static long vwsnd_mixer_ioctl(struct file *file,
 			      unsigned int cmd,
 			      unsigned long arg)
 {
@@ -3215,6 +3223,7 @@
 
 	DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
 
+	lock_kernel();
 	mutex_lock(&devc->mix_mutex);
 	{
 		if ((cmd & ~nrmask) == MIXER_READ(0))
@@ -3225,13 +3234,14 @@
 			retval = -EINVAL;
 	}
 	mutex_unlock(&devc->mix_mutex);
+	unlock_kernel();
 	return retval;
 }
 
 static const struct file_operations vwsnd_mixer_fops = {
 	.owner =	THIS_MODULE,
 	.llseek =	no_llseek,
-	.ioctl =	vwsnd_mixer_ioctl,
+	.unlocked_ioctl = vwsnd_mixer_ioctl,
 	.open =		vwsnd_mixer_open,
 	.release =	vwsnd_mixer_release,
 };
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
index e688dde..5246874 100644
--- a/sound/oss/waveartist.c
+++ b/sound/oss/waveartist.c
@@ -184,14 +184,8 @@
 static inline int
 waveartist_sleep(int timeout_ms)
 {
-	unsigned int timeout = timeout_ms * 10 * HZ / 100;
-
-	do {
-		set_current_state(TASK_INTERRUPTIBLE);
-		timeout = schedule_timeout(timeout);
-	} while (timeout);
-
-	return 0;
+	unsigned int timeout = msecs_to_jiffies(timeout_ms*100);
+	return schedule_timeout_interruptible(timeout);
 }
 
 static int
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 6cf1de8..0e247cb 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -763,9 +763,9 @@
 	/* SPECS_PAGE: 39 */
 	for (i = ALS4K_GCR91_DMA0_ADDR; i <= ALS4K_GCR96_DMA3_MODE_COUNT; ++i)
 		snd_als4k_gcr_write(chip, i, 0);
-	
+	/* enable burst mode to prevent dropouts during high PCI bus usage */
 	snd_als4k_gcr_write(chip, ALS4K_GCR99_DMA_EMULATION_CTRL,
-		snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL));
+		(snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL) & ~0x07) | 0x04);
 	spin_unlock_irq(&chip->reg_lock);
 }
 
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index 1db586a..c80b0b8 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -460,6 +460,7 @@
 	struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
 	int err;
 	u16 format;
+	int width;
 	unsigned int bytes_per_sec;
 
 	print_hwparams(params);
@@ -512,9 +513,10 @@
 				dpcm->hpi_buffer_attached);
 	}
 	bytes_per_sec = params_rate(params) * params_channels(params);
-	bytes_per_sec *= snd_pcm_format_width(params_format(params));
+	width = snd_pcm_format_width(params_format(params));
+	bytes_per_sec *= width;
 	bytes_per_sec /= 8;
-	if (bytes_per_sec <= 0)
+	if (width < 0 || bytes_per_sec == 0)
 		return -EINVAL;
 
 	dpcm->bytes_per_sec = bytes_per_sec;
@@ -1383,7 +1385,7 @@
 
 compile_time_assert(
 	(ARRAY_SIZE(asihpi_src_names) ==
-		(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_BASE+1)),
+		(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)),
 	assert_src_names_size);
 
 #if ASI_STYLE_NAMES
@@ -1414,7 +1416,7 @@
 
 compile_time_assert(
 	(ARRAY_SIZE(asihpi_dst_names) ==
-		(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_BASE+1)),
+		(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)),
 	assert_dst_names_size);
 
 static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl,
@@ -2171,7 +2173,7 @@
 					&src_node_type, &src_node_index);
 
 	sprintf(uinfo->value.enumerated.name, "%s %d",
-		asihpi_src_names[src_node_type - HPI_SOURCENODE_BASE],
+		asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE],
 		src_node_index);
 	return 0;
 }
@@ -2603,8 +2605,8 @@
 
 		}
 
-		hpi_ctl.src_node_type -= HPI_SOURCENODE_BASE;
-		hpi_ctl.dst_node_type -= HPI_DESTNODE_BASE;
+		hpi_ctl.src_node_type -= HPI_SOURCENODE_NONE;
+		hpi_ctl.dst_node_type -= HPI_DESTNODE_NONE;
 
 		/* ASI50xx in SSX mode has multiple meters on the same node.
 		   Use subindex to create distinct ALSA controls
diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h
index 0173bbe..23399d0 100644
--- a/sound/pci/asihpi/hpi.h
+++ b/sound/pci/asihpi/hpi.h
@@ -50,7 +50,8 @@
 #define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
 
 /* Use single digits for versions less that 10 to avoid octal. */
-#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 3, 25)
+#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 4, 1)
+#define HPI_VER_STRING "4.04.01"
 
 /* Library version as documented in hpi-api-versions.txt */
 #define HPI_LIB_VER  HPI_VERSION_CONSTRUCTOR(9, 0, 0)
@@ -203,8 +204,6 @@
 	exists on a destination node can be searched for using a source
 	node value of either 0, or HPI_SOURCENODE_NONE */
 	HPI_SOURCENODE_NONE = 100,
-	/** \deprecated Use HPI_SOURCENODE_NONE instead. */
-	HPI_SOURCENODE_BASE = 100,
 	/** Out Stream (Play) node. */
 	HPI_SOURCENODE_OSTREAM = 101,
 	/** Line in node - could be analog, AES/EBU or network. */
@@ -235,8 +234,6 @@
 	exists on a source node can be searched for using a destination
 	node value of either 0, or HPI_DESTNODE_NONE */
 	HPI_DESTNODE_NONE = 200,
-	/** \deprecated Use HPI_DESTNODE_NONE instead. */
-	HPI_DESTNODE_BASE = 200,
 	/** In Stream (Record) node. */
 	HPI_DESTNODE_ISTREAM = 201,
 	HPI_DESTNODE_LINEOUT = 202,	    /**< line out node. */
@@ -432,7 +429,18 @@
 Property 1 - adapter can do samplerate conversion (MRX)
 Property 2 - adapter can do timestretch (TSX)
 */
-	HPI_ADAPTER_PROPERTY_CAPS2 = 269
+	HPI_ADAPTER_PROPERTY_CAPS2 = 269,
+
+/** Readonly adapter sync header connection count.
+*/
+	HPI_ADAPTER_PROPERTY_SYNC_HEADER_CONNECTIONS = 270,
+/** Readonly supports SSX2 property.
+Indicates the adapter supports SSX2 in some mode setting. The
+return value is true (1) or false (0). If the current adapter
+mode is MONO SSX2 is disabled, even though this property will
+return true.
+*/
+	HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271
 };
 
 /** Adapter mode commands
@@ -813,8 +821,6 @@
 /** The sampleclock output is derived from its local samplerate generator.
     The local samplerate may be set using HPI_SampleClock_SetLocalRate(). */
 	HPI_SAMPLECLOCK_SOURCE_LOCAL = 1,
-/** \deprecated Use HPI_SAMPLECLOCK_SOURCE_LOCAL instead */
-	HPI_SAMPLECLOCK_SOURCE_ADAPTER = 1,
 /** The adapter is clocked from a dedicated AES/EBU SampleClock input.*/
 	HPI_SAMPLECLOCK_SOURCE_AESEBU_SYNC = 2,
 /** From external wordclock connector */
@@ -825,10 +831,6 @@
 	HPI_SAMPLECLOCK_SOURCE_SMPTE = 5,
 /** One of the aesebu inputs */
 	HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT = 6,
-/** \deprecated The first aesebu input with a valid signal
-Superseded by separate Auto enable flag
-*/
-	HPI_SAMPLECLOCK_SOURCE_AESEBU_AUTO = 7,
 /** From a network interface e.g. Cobranet or Livewire at either 48 or 96kHz */
 	HPI_SAMPLECLOCK_SOURCE_NETWORK = 8,
 /** From previous adjacent module (ASI2416 only)*/
@@ -1015,8 +1017,6 @@
 	HPI_ERROR_CONTROL_DISABLED = 404,
 	/** I2C transaction failed due to a missing ACK. */
 	HPI_ERROR_CONTROL_I2C_MISSING_ACK = 405,
-	/** Control attribute is valid, but not supported by this hardware. */
-	HPI_ERROR_UNSUPPORTED_CONTROL_ATTRIBUTE = 406,
 	/** Control is busy, or coming out of
 	reset and cannot be accessed at this time. */
 	HPI_ERROR_CONTROL_NOT_READY = 407,
@@ -1827,13 +1827,41 @@
   Compressor Expander control
 *******************************/
 
-u16 hpi_compander_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-	u16 attack, u16 decay, short ratio100, short threshold0_01dB,
-	short makeup_gain0_01dB);
+u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 on);
 
-u16 hpi_compander_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-	u16 *pw_attack, u16 *pw_decay, short *pw_ratio100,
-	short *pn_threshold0_01dB, short *pn_makeup_gain0_01dB);
+u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 *pon);
+
+u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, short makeup_gain0_01dB);
+
+u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, short *pn_makeup_gain0_01dB);
+
+u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
+	*ph_subsys, u32 h_control, u32 index, u32 attack);
+
+u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
+	*ph_subsys, u32 h_control, u32 index, u32 *pw_attack);
+
+u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, u32 decay);
+
+u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, u32 *pw_decay);
+
+u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, short threshold0_01dB);
+
+u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, short *pn_threshold0_01dB);
+
+u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, u32 ratio100);
+
+u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, u32 *pw_ratio100);
 
 /*******************************
   Cobranet HMI control
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index 12dab5e..f7e374e 100644
--- a/sound/pci/asihpi/hpi6000.c
+++ b/sound/pci/asihpi/hpi6000.c
@@ -687,6 +687,7 @@
 	switch (pao->pci.subsys_device_id) {
 	case 0x5100:
 	case 0x5110:	/* ASI5100 revB or higher with C6711D */
+	case 0x5200:	/* ASI5200 PC_ie version of ASI5100 */
 	case 0x6100:
 	case 0x6200:
 		boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200);
@@ -1133,6 +1134,12 @@
 						subsys_device_id) ==
 					HPI_ADAPTER_FAMILY_ASI(0x5100))
 					mask = 0x00000000L;
+				/* ASI5200 uses AX6 code, */
+				/* but has no PLD r/w register to test */
+				if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
+						subsys_device_id) ==
+					HPI_ADAPTER_FAMILY_ASI(0x5200))
+					mask = 0x00000000L;
 				break;
 			case HPI_ADAPTER_FAMILY_ASI(0x8800):
 				/* ASI8800 has 16bit path to FPGA */
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index fdd0ce0..16f502d 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -104,9 +104,9 @@
 #define STR_ROLE_FIELD_MAX 255U
 
 struct hpi_entity_str {
-	uint16_t size;
-	uint8_t type;
-	uint8_t role;
+	u16 size;
+	u8 type;
+	u8 role;
 };
 
 #if defined(_MSC_VER)
@@ -119,11 +119,11 @@
 #if ! defined(HPI_OS_DSP_C6000) || (defined(HPI_OS_DSP_C6000) && (__TI_COMPILER_VERSION__ > 6000008))
 	/* DSP C6000 compiler v6.0.8 and lower
 	   do not support  flexible array member */
-	uint8_t value[];
+	u8 value[];
 #else
 	/* NOTE! Using sizeof(struct hpi_entity) will give erroneous results */
 #define HPI_INTERNAL_WARN_ABOUT_ENTITY_VALUE
-	uint8_t value[1];
+	u8 value[1];
 #endif
 };
 
@@ -142,12 +142,15 @@
 /******************************************* CONTROL ATTRIBUTES ****/
 /* (in order of control type ID */
 
-	/* This allows for 255 control types, 256 unique attributes each */
+/* This allows for 255 control types, 256 unique attributes each */
 #define HPI_CTL_ATTR(ctl, ai) (HPI_CONTROL_##ctl * 0x100 + ai)
 
 /* Get the sub-index of the attribute for a control type */
 #define HPI_CTL_ATTR_INDEX(i) (i&0xff)
 
+/* Extract the control from the control attribute */
+#define HPI_CTL_ATTR_CONTROL(i) (i>>8)
+
 /* Generic control attributes.  */
 
 /** Enable a control.
@@ -311,8 +314,7 @@
 /* Microphone control attributes */
 #define HPI_MICROPHONE_PHANTOM_POWER HPI_CTL_ATTR(MICROPHONE, 1)
 
-/** Equalizer control attributes
-*/
+/** Equalizer control attributes */
 /** Used to get number of filters in an EQ. (Can't set) */
 #define HPI_EQUALIZER_NUM_FILTERS HPI_CTL_ATTR(EQUALIZER, 1)
 /** Set/get the filter by type, freq, Q, gain */
@@ -320,13 +322,15 @@
 /** Get the biquad coefficients */
 #define HPI_EQUALIZER_COEFFICIENTS HPI_CTL_ATTR(EQUALIZER, 3)
 
-#define HPI_COMPANDER_PARAMS HPI_CTL_ATTR(COMPANDER, 1)
+/* Note compander also uses HPI_GENERIC_ENABLE */
+#define HPI_COMPANDER_PARAMS     HPI_CTL_ATTR(COMPANDER, 1)
+#define HPI_COMPANDER_MAKEUPGAIN HPI_CTL_ATTR(COMPANDER, 2)
+#define HPI_COMPANDER_THRESHOLD  HPI_CTL_ATTR(COMPANDER, 3)
+#define HPI_COMPANDER_RATIO      HPI_CTL_ATTR(COMPANDER, 4)
+#define HPI_COMPANDER_ATTACK     HPI_CTL_ATTR(COMPANDER, 5)
+#define HPI_COMPANDER_DECAY      HPI_CTL_ATTR(COMPANDER, 6)
 
-/* Cobranet control attributes.
-   MUST be distinct from all other control attributes.
-   This is so that host side processing can easily identify a Cobranet control
-   and apply additional host side operations (like copying data) as required.
-*/
+/* Cobranet control attributes. */
 #define HPI_COBRANET_SET         HPI_CTL_ATTR(COBRANET, 1)
 #define HPI_COBRANET_GET         HPI_CTL_ATTR(COBRANET, 2)
 #define HPI_COBRANET_SET_DATA    HPI_CTL_ATTR(COBRANET, 3)
@@ -1512,11 +1516,11 @@
 	struct hpi_control_cache_info i;
 	union {
 		struct {	/* volume */
-			u16 an_log[2];
+			short an_log[2];
 		} v;
 		struct {	/* peak meter */
-			u16 an_log_peak[2];
-			u16 an_logRMS[2];
+			short an_log_peak[2];
+			short an_logRMS[2];
 		} p;
 		struct {	/* channel mode */
 			u16 mode;
@@ -1526,7 +1530,7 @@
 			u16 source_node_index;
 		} x;
 		struct {	/* level/trim */
-			u16 an_log[2];
+			short an_log[2];
 		} l;
 		struct {	/* tuner - partial caching.
 				   some attributes go to the DSP. */
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index fcd6453..dda4f1c 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -353,7 +353,12 @@
 			phr->u.c.param1 = pC->u.t.band;
 		else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
 			&& (phm->u.c.param1 == HPI_TUNER_LEVEL_AVERAGE))
-			phr->u.c.param1 = pC->u.t.level;
+			if (pC->u.t.level == HPI_ERROR_ILLEGAL_CACHE_VALUE) {
+				phr->u.c.param1 = 0;
+				phr->error =
+					HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+			} else
+				phr->u.c.param1 = pC->u.t.level;
 		else
 			found = 0;
 		break;
@@ -397,7 +402,8 @@
 			if (pC->u.clk.source_index ==
 				HPI_ERROR_ILLEGAL_CACHE_VALUE) {
 				phr->u.c.param1 = 0;
-				phr->error = HPI_ERROR_INVALID_OPERATION;
+				phr->error =
+					HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
 			} else
 				phr->u.c.param1 = pC->u.clk.source_index;
 		} else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)
diff --git a/sound/pci/asihpi/hpidebug.c b/sound/pci/asihpi/hpidebug.c
index 4cd85a4..949836e 100644
--- a/sound/pci/asihpi/hpidebug.c
+++ b/sound/pci/asihpi/hpidebug.c
@@ -111,7 +111,7 @@
   &hpi_profile_strings,\
   &hpi_control_strings, \
   &hpi_asyncevent_strings \
-};
+}
 	make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS)
 
 	compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match);
diff --git a/sound/pci/asihpi/hpidebug.h b/sound/pci/asihpi/hpidebug.h
index 44dccad..a2f0952 100644
--- a/sound/pci/asihpi/hpidebug.h
+++ b/sound/pci/asihpi/hpidebug.h
@@ -356,7 +356,7 @@
 	"HPI_SOURCENODE_ADAPTER" \
 }
 
-compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_BASE + 1) ==
+compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_NONE + 1) ==
 	(12), sourcenode_strings_match_defs);
 
 #define HPI_DESTNODE_STRINGS \
@@ -370,7 +370,7 @@
 	"HPI_DESTNODE_COBRANET", \
 	"HPI_DESTNODE_ANALOG" \
 }
-compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_BASE + 1) == (8),
+compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_NONE + 1) == (8),
 	destnode_strings_match_defs);
 
 #define HPI_CONTROL_CHANNEL_MODE_STRINGS \
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index 298eef3..1e92eb6 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -96,8 +96,7 @@
 
 static struct hpi_hsubsys gh_subsys;
 
-struct hpi_hsubsys *hpi_subsys_create(void
-	)
+struct hpi_hsubsys *hpi_subsys_create(void)
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
@@ -302,6 +301,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
 		HPI_ADAPTER_SET_MODE);
 	hm.adapter_index = adapter_index;
@@ -510,7 +510,7 @@
 	hm.adapter_index = adapter_index;
 	hm.u.ax.debug_read.dsp_address = dsp_address;
 
-	if (*count_bytes > sizeof(hr.u.bytes))
+	if (*count_bytes > (int)sizeof(hr.u.bytes))
 		*count_bytes = sizeof(hr.u.bytes);
 
 	hm.u.ax.debug_read.count_bytes = *count_bytes;
@@ -976,6 +976,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
 		HPI_OSTREAM_ANC_READ);
 	u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
@@ -1581,6 +1582,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_SET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -1591,6 +1593,22 @@
 	return hr.error;
 }
 
+static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0,
+	short sv1)
+{
+	struct hpi_message hm;
+	struct hpi_response hr;
+
+	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
+		HPI_CONTROL_SET_STATE);
+	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+	hm.u.c.attribute = attrib;
+	hm.u.c.an_log_value[0] = sv0;
+	hm.u.c.an_log_value[1] = sv1;
+	hpi_send_recv(&hm, &hr);
+	return hr.error;
+}
+
 static
 u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys,
 	const u32 h_control, const u16 attrib, u32 param1, u32 param2,
@@ -1598,6 +1616,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -1605,8 +1624,8 @@
 	hm.u.c.param1 = param1;
 	hm.u.c.param2 = param2;
 	hpi_send_recv(&hm, &hr);
-	if (pparam1)
-		*pparam1 = hr.u.c.param1;
+
+	*pparam1 = hr.u.c.param1;
 	if (pparam2)
 		*pparam2 = hr.u.c.param2;
 
@@ -1617,10 +1636,23 @@
 		hpi_control_param_get(s, h, a, 0, 0, p1, NULL)
 #define hpi_control_param2_get(s, h, a, p1, p2) \
 		hpi_control_param_get(s, h, a, 0, 0, p1, p2)
-#define hpi_control_ex_param1_get(s, h, a, p1) \
-		hpi_control_ex_param_get(s, h, a, 0, 0, p1, NULL)
-#define hpi_control_ex_param2_get(s, h, a, p1, p2) \
-		hpi_control_ex_param_get(s, h, a, 0, 0, p1, p2)
+
+static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u16 attrib, short *sv0, short *sv1)
+{
+	struct hpi_message hm;
+	struct hpi_response hr;
+	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
+		HPI_CONTROL_GET_STATE);
+	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+	hm.u.c.attribute = attrib;
+
+	hpi_send_recv(&hm, &hr);
+	*sv0 = hr.u.c.an_log_value[0];
+	if (sv1)
+		*sv1 = hr.u.c.an_log_value[1];
+	return hr.error;
+}
 
 static
 u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys,
@@ -1629,6 +1661,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_INFO);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -1643,9 +1676,8 @@
 	return hr.error;
 }
 
-static u16 hpi_control_get_string(const struct hpi_hsubsys *ph_subsys,
-	const u32 h_control, const u16 attribute, char *psz_string,
-	const u32 string_length)
+static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
+	char *psz_string, const u32 string_length)
 {
 	unsigned int sub_string_index = 0, j = 0;
 	char c = 0;
@@ -1916,6 +1948,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
 		HPI_CONTROL_SET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -1941,6 +1974,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -1980,6 +2014,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2006,6 +2041,7 @@
 	u32 byte_count;
 	u32 iP;
 	u16 error;
+
 	error = hpi_cobranet_hmi_read(ph_subsys, h_control,
 		HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, &byte_count,
 		(u8 *)&iP);
@@ -2082,6 +2118,7 @@
 	u32 byte_count;
 	u16 error;
 	u32 mAC;
+
 	error = hpi_cobranet_hmi_read(ph_subsys, h_control,
 		HPI_COBRANET_HMI_cobra_if_phy_address, 4, &byte_count,
 		(u8 *)&mAC);
@@ -2103,60 +2140,119 @@
 	return error;
 }
 
-u16 hpi_compander_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-	u16 attack, u16 decay, short ratio100, short threshold0_01dB,
-	short makeup_gain0_01dB)
+u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 enable)
+{
+	return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
+		enable, 0);
+}
+
+u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 *enable)
+{
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_GENERIC_ENABLE, enable);
+}
+
+u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, short makeup_gain0_01dB)
+{
+	return hpi_control_log_set2(h_control, HPI_COMPANDER_MAKEUPGAIN,
+		makeup_gain0_01dB, 0);
+}
+
+u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, short *makeup_gain0_01dB)
+{
+	return hpi_control_log_get2(ph_subsys, h_control,
+		HPI_COMPANDER_MAKEUPGAIN, makeup_gain0_01dB, NULL);
+}
+
+u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
+	*ph_subsys, u32 h_control, unsigned int index, u32 attack)
+{
+	return hpi_control_param_set(ph_subsys, h_control,
+		HPI_COMPANDER_ATTACK, attack, index);
+}
+
+u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
+	*ph_subsys, u32 h_control, unsigned int index, u32 *attack)
+{
+	return hpi_control_param_get(ph_subsys, h_control,
+		HPI_COMPANDER_ATTACK, 0, index, attack, NULL);
+}
+
+u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, unsigned int index, u32 decay)
+{
+	return hpi_control_param_set(ph_subsys, h_control,
+		HPI_COMPANDER_DECAY, decay, index);
+}
+
+u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, unsigned int index, u32 *decay)
+{
+	return hpi_control_param_get(ph_subsys, h_control,
+		HPI_COMPANDER_DECAY, 0, index, decay, NULL);
+
+}
+
+u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, unsigned int index, short threshold0_01dB)
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_SET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-
-	hm.u.c.param1 = attack + ((u32)ratio100 << 16);
-	hm.u.c.param2 = (decay & 0xFFFFL);
+	hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
+	hm.u.c.param2 = index;
 	hm.u.c.an_log_value[0] = threshold0_01dB;
-	hm.u.c.an_log_value[1] = makeup_gain0_01dB;
-	hm.u.c.attribute = HPI_COMPANDER_PARAMS;
 
 	hpi_send_recv(&hm, &hr);
 
 	return hr.error;
 }
 
-u16 hpi_compander_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
-	u16 *pw_attack, u16 *pw_decay, short *pw_ratio100,
-	short *pn_threshold0_01dB, short *pn_makeup_gain0_01dB)
+u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, unsigned int index, short *threshold0_01dB)
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-	hm.u.c.attribute = HPI_COMPANDER_PARAMS;
+	hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
+	hm.u.c.param2 = index;
 
 	hpi_send_recv(&hm, &hr);
-
-	if (pw_attack)
-		*pw_attack = (short)(hr.u.c.param1 & 0xFFFF);
-	if (pw_decay)
-		*pw_decay = (short)(hr.u.c.param2 & 0xFFFF);
-	if (pw_ratio100)
-		*pw_ratio100 = (short)(hr.u.c.param1 >> 16);
-
-	if (pn_threshold0_01dB)
-		*pn_threshold0_01dB = hr.u.c.an_log_value[0];
-	if (pn_makeup_gain0_01dB)
-		*pn_makeup_gain0_01dB = hr.u.c.an_log_value[1];
+	*threshold0_01dB = hr.u.c.an_log_value[0];
 
 	return hr.error;
 }
 
+u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, u32 ratio100)
+{
+	return hpi_control_param_set(ph_subsys, h_control,
+		HPI_COMPANDER_RATIO, ratio100, index);
+}
+
+u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
+	u32 h_control, u32 index, u32 *ratio100)
+{
+	return hpi_control_param_get(ph_subsys, h_control,
+		HPI_COMPANDER_RATIO, 0, index, ratio100, NULL);
+}
+
 u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 	short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB)
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2181,37 +2277,16 @@
 	short an_gain0_01dB[HPI_MAX_CHANNELS]
 	)
 {
-	struct hpi_message hm;
-	struct hpi_response hr;
-
-	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
-		HPI_CONTROL_SET_STATE);
-	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-	memcpy(hm.u.c.an_log_value, an_gain0_01dB,
-		sizeof(short) * HPI_MAX_CHANNELS);
-	hm.u.c.attribute = HPI_LEVEL_GAIN;
-
-	hpi_send_recv(&hm, &hr);
-
-	return hr.error;
+	return hpi_control_log_set2(h_control, HPI_LEVEL_GAIN,
+		an_gain0_01dB[0], an_gain0_01dB[1]);
 }
 
 u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 	short an_gain0_01dB[HPI_MAX_CHANNELS]
 	)
 {
-	struct hpi_message hm;
-	struct hpi_response hr;
-	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
-		HPI_CONTROL_GET_STATE);
-	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-	hm.u.c.attribute = HPI_LEVEL_GAIN;
-
-	hpi_send_recv(&hm, &hr);
-
-	memcpy(an_gain0_01dB, hr.u.c.an_log_value,
-		sizeof(short) * HPI_MAX_CHANNELS);
-	return hr.error;
+	return hpi_control_log_get2(ph_subsys, h_control, HPI_LEVEL_GAIN,
+		&an_gain0_01dB[0], &an_gain0_01dB[1]);
 }
 
 u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
@@ -2413,6 +2488,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2439,6 +2515,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_SET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2460,6 +2537,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2623,8 +2701,8 @@
 u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *state)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_TONEDETECTOR_STATE, 0, 0, (u32 *)state, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_TONEDETECTOR_STATE, state);
 }
 
 u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
@@ -2637,8 +2715,8 @@
 u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *enable)
 {
-	return hpi_control_param_get(ph_subsys, h_control, HPI_GENERIC_ENABLE,
-		0, 0, (u32 *)enable, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_GENERIC_ENABLE, enable);
 }
 
 u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
@@ -2651,8 +2729,8 @@
 u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *event_enable)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_GENERIC_EVENT_ENABLE, 0, 0, (u32 *)event_enable, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_GENERIC_EVENT_ENABLE, event_enable);
 }
 
 u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
@@ -2665,15 +2743,15 @@
 u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, int *threshold)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_TONEDETECTOR_THRESHOLD, 0, 0, (u32 *)threshold, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_TONEDETECTOR_THRESHOLD, (u32 *)threshold);
 }
 
 u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *state)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_SILENCEDETECTOR_STATE, 0, 0, (u32 *)state, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_SILENCEDETECTOR_STATE, state);
 }
 
 u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
@@ -2686,50 +2764,50 @@
 u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *enable)
 {
-	return hpi_control_param_get(ph_subsys, h_control, HPI_GENERIC_ENABLE,
-		0, 0, (u32 *)enable, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_GENERIC_ENABLE, enable);
 }
 
 u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 event_enable)
 {
 	return hpi_control_param_set(ph_subsys, h_control,
-		HPI_GENERIC_EVENT_ENABLE, (u32)event_enable, 0);
+		HPI_GENERIC_EVENT_ENABLE, event_enable, 0);
 }
 
 u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *event_enable)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_GENERIC_EVENT_ENABLE, 0, 0, (u32 *)event_enable, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_GENERIC_EVENT_ENABLE, event_enable);
 }
 
 u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 delay)
 {
 	return hpi_control_param_set(ph_subsys, h_control,
-		HPI_SILENCEDETECTOR_DELAY, (u32)delay, 0);
+		HPI_SILENCEDETECTOR_DELAY, delay, 0);
 }
 
 u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *delay)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_SILENCEDETECTOR_DELAY, 0, 0, (u32 *)delay, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_SILENCEDETECTOR_DELAY, delay);
 }
 
 u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, int threshold)
 {
 	return hpi_control_param_set(ph_subsys, h_control,
-		HPI_SILENCEDETECTOR_THRESHOLD, (u32)threshold, 0);
+		HPI_SILENCEDETECTOR_THRESHOLD, threshold, 0);
 }
 
 u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, int *threshold)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_SILENCEDETECTOR_THRESHOLD, 0, 0, (u32 *)threshold, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_SILENCEDETECTOR_THRESHOLD, (u32 *)threshold);
 }
 
 u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
@@ -2822,6 +2900,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2838,6 +2917,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2894,14 +2974,14 @@
 u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, char *psz_dsp_version, const u32 string_size)
 {
-	return hpi_control_get_string(ph_subsys, h_control,
+	return hpi_control_get_string(h_control,
 		HPI_TUNER_HDRADIO_DSP_VERSION, psz_dsp_version, string_size);
 }
 
 u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, char *psz_sdk_version, const u32 string_size)
 {
-	return hpi_control_get_string(ph_subsys, h_control,
+	return hpi_control_get_string(h_control,
 		HPI_TUNER_HDRADIO_SDK_VERSION, psz_sdk_version, string_size);
 }
 
@@ -2942,15 +3022,15 @@
 u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *pquality)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_TUNER_HDRADIO_SIGNAL_QUALITY, 0, 0, pquality, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_TUNER_HDRADIO_SIGNAL_QUALITY, pquality);
 }
 
 u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *pblend)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_TUNER_HDRADIO_BLEND, 0, 0, pblend, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_TUNER_HDRADIO_BLEND, pblend);
 }
 
 u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
@@ -2965,6 +3045,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -2981,43 +3062,43 @@
 u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, char *psz_string, const u32 data_length)
 {
-	return hpi_control_get_string(ph_subsys, h_control,
-		HPI_PAD_CHANNEL_NAME, psz_string, data_length);
+	return hpi_control_get_string(h_control, HPI_PAD_CHANNEL_NAME,
+		psz_string, data_length);
 }
 
 u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 	char *psz_string, const u32 data_length)
 {
-	return hpi_control_get_string(ph_subsys, h_control, HPI_PAD_ARTIST,
-		psz_string, data_length);
+	return hpi_control_get_string(h_control, HPI_PAD_ARTIST, psz_string,
+		data_length);
 }
 
 u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 	char *psz_string, const u32 data_length)
 {
-	return hpi_control_get_string(ph_subsys, h_control, HPI_PAD_TITLE,
-		psz_string, data_length);
+	return hpi_control_get_string(h_control, HPI_PAD_TITLE, psz_string,
+		data_length);
 }
 
 u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 	char *psz_string, const u32 data_length)
 {
-	return hpi_control_get_string(ph_subsys, h_control, HPI_PAD_COMMENT,
-		psz_string, data_length);
+	return hpi_control_get_string(h_control, HPI_PAD_COMMENT, psz_string,
+		data_length);
 }
 
 u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
 	u32 h_control, u32 *ppTY)
 {
-	return hpi_control_param_get(ph_subsys, h_control,
-		HPI_PAD_PROGRAM_TYPE, 0, 0, ppTY, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_PAD_PROGRAM_TYPE, ppTY);
 }
 
 u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 	u32 *ppI)
 {
-	return hpi_control_param_get(ph_subsys, h_control, HPI_PAD_PROGRAM_ID,
-		0, 0, ppI, NULL);
+	return hpi_control_param1_get(ph_subsys, h_control,
+		HPI_PAD_PROGRAM_ID, ppI);
 }
 
 u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
@@ -3031,36 +3112,16 @@
 	short an_log_gain[HPI_MAX_CHANNELS]
 	)
 {
-	struct hpi_message hm;
-	struct hpi_response hr;
-	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
-		HPI_CONTROL_SET_STATE);
-	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-	memcpy(hm.u.c.an_log_value, an_log_gain,
-		sizeof(short) * HPI_MAX_CHANNELS);
-	hm.u.c.attribute = HPI_VOLUME_GAIN;
-
-	hpi_send_recv(&hm, &hr);
-
-	return hr.error;
+	return hpi_control_log_set2(h_control, HPI_VOLUME_GAIN,
+		an_log_gain[0], an_log_gain[1]);
 }
 
 u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
 	short an_log_gain[HPI_MAX_CHANNELS]
 	)
 {
-	struct hpi_message hm;
-	struct hpi_response hr;
-	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
-		HPI_CONTROL_GET_STATE);
-	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
-	hm.u.c.attribute = HPI_VOLUME_GAIN;
-
-	hpi_send_recv(&hm, &hr);
-
-	memcpy(an_log_gain, hr.u.c.an_log_value,
-		sizeof(short) * HPI_MAX_CHANNELS);
-	return hr.error;
+	return hpi_control_log_get2(ph_subsys, h_control, HPI_VOLUME_GAIN,
+		&an_log_gain[0], &an_log_gain[1]);
 }
 
 u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
@@ -3068,6 +3129,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_GET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -3094,6 +3156,7 @@
 {
 	struct hpi_message hm;
 	struct hpi_response hr;
+
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
 		HPI_CONTROL_SET_STATE);
 	u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
@@ -3170,43 +3233,42 @@
 	6 * sizeof(char),
 };
 
-inline size_t hpi_entity_size(struct hpi_entity *entity_ptr)
+static inline size_t hpi_entity_size(struct hpi_entity *entity_ptr)
 {
 	return entity_ptr->header.size;
 }
 
-inline size_t hpi_entity_header_size(struct hpi_entity *entity_ptr)
+static inline size_t hpi_entity_header_size(struct hpi_entity *entity_ptr)
 {
 	return sizeof(entity_ptr->header);
 }
 
-inline size_t hpi_entity_value_size(struct hpi_entity *entity_ptr)
+static inline size_t hpi_entity_value_size(struct hpi_entity *entity_ptr)
 {
 	return hpi_entity_size(entity_ptr) -
 		hpi_entity_header_size(entity_ptr);
 }
 
-inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr)
+static inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr)
 {
 	return hpi_entity_value_size(entity_ptr) /
 		entity_type_to_size[entity_ptr->header.type];
 }
 
-inline struct hpi_entity *hpi_entity_ptr_to_next(struct hpi_entity
+static inline struct hpi_entity *hpi_entity_ptr_to_next(struct hpi_entity
 	*entity_ptr)
 {
-	return (void *)(((uint8_t *) entity_ptr) +
-		hpi_entity_size(entity_ptr));
+	return (void *)(((u8 *)entity_ptr) + hpi_entity_size(entity_ptr));
 }
 
-inline u16 hpi_entity_check_type(const enum e_entity_type t)
+static inline u16 hpi_entity_check_type(const enum e_entity_type t)
 {
 	if (t >= 0 && t < STR_TYPE_FIELD_MAX)
 		return 0;
 	return HPI_ERROR_ENTITY_TYPE_INVALID;
 }
 
-inline u16 hpi_entity_check_role(const enum e_entity_role r)
+static inline u16 hpi_entity_check_role(const enum e_entity_role r)
 {
 	if (r >= 0 && r < STR_ROLE_FIELD_MAX)
 		return 0;
@@ -3624,6 +3686,7 @@
 	u16 maximum_events, struct hpi_async_event *p_events,
 	u16 *pw_number_returned)
 {
+
 	return 0;
 }
 
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index 2ee90dc..f01ab96 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -741,7 +741,7 @@
 		hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM,
 			HPI_SUBSYS_FIND_ADAPTERS, 0);
 		memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr,
-			sizeof(&gRESP_HPI_SUBSYS_FIND_ADAPTERS));
+			sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
 
 		for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
 
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 7396ac5..62895a7 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -121,11 +121,17 @@
 	phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
 
 	/* Read the message and response pointers from user space.  */
-	get_user(puhm, &phpi_ioctl_data->phm);
-	get_user(puhr, &phpi_ioctl_data->phr);
+	if (get_user(puhm, &phpi_ioctl_data->phm) ||
+	    get_user(puhr, &phpi_ioctl_data->phr)) {
+		err = -EFAULT;
+		goto out;
+	}
 
 	/* Now read the message size and data from user space.  */
-	get_user(hm->h.size, (u16 __user *)puhm);
+	if (get_user(hm->h.size, (u16 __user *)puhm)) {
+		err = -EFAULT;
+		goto out;
+	}
 	if (hm->h.size > sizeof(*hm))
 		hm->h.size = sizeof(*hm);
 
@@ -138,7 +144,10 @@
 		goto out;
 	}
 
-	get_user(res_max_size, (u16 __user *)puhr);
+	if (get_user(res_max_size, (u16 __user *)puhr)) {
+		err = -EFAULT;
+		goto out;
+	}
 	/* printk(KERN_INFO "user response size %d\n", res_max_size); */
 	if (res_max_size < sizeof(struct hpi_response_header)) {
 		HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size);
@@ -464,9 +473,7 @@
 
 	memset(adapters, 0, sizeof(adapters));
 
-	printk(KERN_INFO "ASIHPI driver %d.%02d.%02d\n",
-		HPI_VER_MAJOR(HPI_VER), HPI_VER_MINOR(HPI_VER),
-		HPI_VER_RELEASE(HPI_VER));
+	printk(KERN_INFO "ASIHPI driver " HPI_VER_STRING "\n");
 
 	hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
 		HPI_SUBSYS_DRIVER_LOAD);
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 668a5ec..20763dd0 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -2250,6 +2250,8 @@
 	DE_INIT(("resume start\n"));
 	pci_restore_state(pci);
 	commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL);
+	if (commpage_bak == NULL)
+		return -ENOMEM;
 	commpage = chip->comm_page;
 	memcpy(commpage_bak, commpage, sizeof(struct comm_page));
 
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index ba2098d..a7802b9 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -396,15 +396,18 @@
 			}
 			for (n = prev_nid + 1; n <= val; n++) {
 				if (conns >= max_conns) {
-					snd_printk(KERN_ERR
-						   "Too many connections\n");
+					snd_printk(KERN_ERR "hda_codec: "
+						   "Too many connections %d for NID 0x%x\n",
+						   conns, nid);
 					return -EINVAL;
 				}
 				conn_list[conns++] = n;
 			}
 		} else {
 			if (conns >= max_conns) {
-				snd_printk(KERN_ERR "Too many connections\n");
+				snd_printk(KERN_ERR "hda_codec: "
+					   "Too many connections %d for NID 0x%x\n",
+					   conns, nid);
 				return -EINVAL;
 			}
 			conn_list[conns++] = val;
@@ -730,15 +733,17 @@
 	total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
 	for (i = 0; i < total_nodes; i++, nid++) {
 		function_id = snd_hda_param_read(codec, nid,
-						AC_PAR_FUNCTION_TYPE) & 0xff;
-		switch (function_id) {
+						AC_PAR_FUNCTION_TYPE);
+		switch (function_id & 0xff) {
 		case AC_GRP_AUDIO_FUNCTION:
 			codec->afg = nid;
-			codec->function_id = function_id;
+			codec->afg_function_id = function_id & 0xff;
+			codec->afg_unsol = (function_id >> 8) & 1;
 			break;
 		case AC_GRP_MODEM_FUNCTION:
 			codec->mfg = nid;
-			codec->function_id = function_id;
+			codec->mfg_function_id = function_id & 0xff;
+			codec->mfg_unsol = (function_id >> 8) & 1;
 			break;
 		default:
 			break;
@@ -1565,6 +1570,17 @@
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
+static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
+			     unsigned int ofs)
+{
+	u32 caps = query_amp_caps(codec, nid, dir);
+	/* get num steps */
+	caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+	if (ofs < caps)
+		caps -= ofs;
+	return caps;
+}
+
 /**
  * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
  *
@@ -1579,23 +1595,17 @@
 	u8 chs = get_amp_channels(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	unsigned int ofs = get_amp_offset(kcontrol);
-	u32 caps;
 
-	caps = query_amp_caps(codec, nid, dir);
-	/* num steps */
-	caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-	if (!caps) {
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = chs == 3 ? 2 : 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
+	if (!uinfo->value.integer.max) {
 		printk(KERN_WARNING "hda_codec: "
 		       "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
 		       kcontrol->id.name);
 		return -EINVAL;
 	}
-	if (ofs < caps)
-		caps -= ofs;
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = chs == 3 ? 2 : 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = caps;
 	return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
@@ -1620,8 +1630,14 @@
 		 int ch, int dir, int idx, unsigned int ofs,
 		 unsigned int val)
 {
+	unsigned int maxval;
+
 	if (val > 0)
 		val += ofs;
+	/* ofs = 0: raw max value */
+	maxval = get_amp_max_value(codec, nid, dir, 0);
+	if (val > maxval)
+		val = maxval;
 	return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
 					HDA_AMP_VOLMASK, val);
 }
@@ -2999,26 +3015,31 @@
 	unsigned int hda_fmt;
 };
 
+/* rate = base * mult / div */
+#define HDA_RATE(base, mult, div) \
+	(AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
+	 (((div) - 1) << AC_FMT_DIV_SHIFT))
+
 static struct hda_rate_tbl rate_bits[] = {
 	/* rate in Hz, ALSA rate bitmask, HDA format value */
 
 	/* autodetected value used in snd_hda_query_supported_pcm */
-	{ 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */
-	{ 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */
-	{ 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */
-	{ 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */
-	{ 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */
-	{ 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */
-	{ 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */
-	{ 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */
-	{ 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */
-	{ 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */
-	{ 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */
+	{ 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
+	{ 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
+	{ 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
+	{ 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
+	{ 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
+	{ 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
+	{ 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
+	{ 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
+	{ 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
+	{ 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
+	{ 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
 #define AC_PAR_PCM_RATE_BITS	11
 	/* up to bits 10, 384kHZ isn't supported properly */
 
 	/* not autodetected value */
-	{ 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */
+	{ 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
 
 	{ 0 } /* terminator */
 };
@@ -3037,7 +3058,8 @@
 unsigned int snd_hda_calc_stream_format(unsigned int rate,
 					unsigned int channels,
 					unsigned int format,
-					unsigned int maxbps)
+					unsigned int maxbps,
+					unsigned short spdif_ctls)
 {
 	int i;
 	unsigned int val = 0;
@@ -3060,20 +3082,20 @@
 
 	switch (snd_pcm_format_width(format)) {
 	case 8:
-		val |= 0x00;
+		val |= AC_FMT_BITS_8;
 		break;
 	case 16:
-		val |= 0x10;
+		val |= AC_FMT_BITS_16;
 		break;
 	case 20:
 	case 24:
 	case 32:
 		if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
-			val |= 0x40;
+			val |= AC_FMT_BITS_32;
 		else if (maxbps >= 24)
-			val |= 0x30;
+			val |= AC_FMT_BITS_24;
 		else
-			val |= 0x20;
+			val |= AC_FMT_BITS_20;
 		break;
 	default:
 		snd_printdd("invalid format width %d\n",
@@ -3081,6 +3103,9 @@
 		return 0;
 	}
 
+	if (spdif_ctls & AC_DIG1_NONAUDIO)
+		val |= AC_FMT_TYPE_NON_PCM;
+
 	return val;
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 5991d14..0328cf5 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -224,6 +224,27 @@
 /* Input converter SDI select */
 #define AC_SDI_SELECT			(0xf<<0)
 
+/* stream format id */
+#define AC_FMT_CHAN_SHIFT		0
+#define AC_FMT_CHAN_MASK		(0x0f << 0)
+#define AC_FMT_BITS_SHIFT		4
+#define AC_FMT_BITS_MASK		(7 << 4)
+#define AC_FMT_BITS_8			(0 << 4)
+#define AC_FMT_BITS_16			(1 << 4)
+#define AC_FMT_BITS_20			(2 << 4)
+#define AC_FMT_BITS_24			(3 << 4)
+#define AC_FMT_BITS_32			(4 << 4)
+#define AC_FMT_DIV_SHIFT		8
+#define AC_FMT_DIV_MASK			(7 << 8)
+#define AC_FMT_MULT_SHIFT		11
+#define AC_FMT_MULT_MASK		(7 << 11)
+#define AC_FMT_BASE_SHIFT		14
+#define AC_FMT_BASE_48K			(0 << 14)
+#define AC_FMT_BASE_44K			(1 << 14)
+#define AC_FMT_TYPE_SHIFT		15
+#define AC_FMT_TYPE_PCM			(0 << 15)
+#define AC_FMT_TYPE_NON_PCM		(1 << 15)
+
 /* Unsolicited response control */
 #define AC_UNSOL_TAG			(0x3f<<0)
 #define AC_UNSOL_ENABLED		(1<<7)
@@ -364,6 +385,9 @@
 #define AC_DIG2_CC			(0x7f<<0)
 
 /* Pin widget control - 8bit */
+#define AC_PINCTL_EPT			(0x3<<0)
+#define AC_PINCTL_EPT_NATIVE		0
+#define AC_PINCTL_EPT_HBR		3
 #define AC_PINCTL_VREFEN		(0x7<<0)
 #define AC_PINCTL_VREF_HIZ		0	/* Hi-Z */
 #define AC_PINCTL_VREF_50		1	/* 50% */
@@ -760,7 +784,10 @@
 	hda_nid_t mfg;	/* MFG node id */
 
 	/* ids */
-	u32 function_id;
+	u8 afg_function_id;
+	u8 mfg_function_id;
+	u8 afg_unsol;
+	u8 mfg_unsol;
 	u32 vendor_id;
 	u32 subsystem_id;
 	u32 revision_id;
@@ -928,7 +955,8 @@
 unsigned int snd_hda_calc_stream_format(unsigned int rate,
 					unsigned int channels,
 					unsigned int format,
-					unsigned int maxbps);
+					unsigned int maxbps,
+					unsigned short spdif_ctls);
 int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
 				unsigned int format);
 
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index a1fc837..bf3ced5 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -649,7 +649,9 @@
 	*codecp = NULL;
 	if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
 		list_for_each_entry(codec, &bus->codec_list, list) {
-			if (codec->addr == caddr) {
+			if (codec->vendor_id == vendorid &&
+			    codec->subsystem_id == subid &&
+			    codec->addr == caddr) {
 				*codecp = codec;
 				break;
 			}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1df25cf..66d4202 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1653,7 +1653,8 @@
 	format_val = snd_hda_calc_stream_format(runtime->rate,
 						runtime->channels,
 						runtime->format,
-						hinfo->maxbps);
+						hinfo->maxbps,
+						apcm->codec->spdif_ctls);
 	if (!format_val) {
 		snd_printk(KERN_ERR SFX
 			   "invalid format_val, rate=%d, ch=%d, format=%d\n",
@@ -1960,7 +1961,7 @@
 		spin_unlock_irq(&chip->reg_lock);
 		if (!pending)
 			return;
-		cond_resched();
+		msleep(1);
 	}
 }
 
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index f97d35d..f025200 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -557,7 +557,12 @@
 	else
 		snd_iprintf(buffer, "Not Set\n");
 	snd_iprintf(buffer, "Address: %d\n", codec->addr);
-	snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
+	if (codec->afg)
+		snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n",
+			codec->afg_function_id, codec->afg_unsol);
+	if (codec->mfg)
+		snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n",
+			codec->mfg_function_id, codec->mfg_unsol);
 	snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
 	snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
 	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index afbe314..b697fd2 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -3662,7 +3662,12 @@
 		codec->patch_ops.build_pcms = ad1984_build_pcms;
 		break;
 	case AD1984_THINKPAD:
-		spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
+		if (codec->subsystem_id == 0x17aa20fb) {
+			/* Thinpad X300 does not have the ability to do SPDIF,
+			   or attach to docking station to use SPDIF */
+			spec->multiout.dig_out_nid = 0;
+		} else
+			spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
 		spec->input_mux = &ad1984_thinkpad_capture_source;
 		spec->mixers[0] = ad1984_thinkpad_mixers;
 		spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 2bf2cb5..df8b19b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -131,6 +131,8 @@
 	unsigned int dc_enable;
 	unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
 	unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
+
+	unsigned int beep_amp;
 };
 
 static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -515,6 +517,15 @@
 	{}
 };
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static struct snd_kcontrol_new cxt_beep_mixer[] = {
+	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+	{ } /* end */
+};
+#endif
+
 static const char *slave_vols[] = {
 	"Headphone Playback Volume",
 	"Speaker Playback Volume",
@@ -580,16 +591,52 @@
 			return err;
 	}
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+	/* create beep controls if needed */
+	if (spec->beep_amp) {
+		struct snd_kcontrol_new *knew;
+		for (knew = cxt_beep_mixer; knew->name; knew++) {
+			struct snd_kcontrol *kctl;
+			kctl = snd_ctl_new1(knew, codec);
+			if (!kctl)
+				return -ENOMEM;
+			kctl->private_value = spec->beep_amp;
+			err = snd_hda_ctl_add(codec, 0, kctl);
+			if (err < 0)
+				return err;
+		}
+	}
+#endif
+
 	return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int conexant_suspend(struct hda_codec *codec, pm_message_t state)
+{
+	snd_hda_shutup_pins(codec);
+	return 0;
+}
+#endif
+
 static struct hda_codec_ops conexant_patch_ops = {
 	.build_controls = conexant_build_controls,
 	.build_pcms = conexant_build_pcms,
 	.init = conexant_init,
 	.free = conexant_free,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	.suspend = conexant_suspend,
+#endif
+	.reboot_notify = snd_hda_shutup_pins,
 };
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define set_beep_amp(spec, nid, idx, dir) \
+	((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
+
 /*
  * EAPD control
  * the private value = nid | (invert << 8)
@@ -1130,9 +1177,10 @@
 	spec->num_init_verbs = 1;
 	spec->init_verbs[0] = cxt5045_init_verbs;
 	spec->spdif_route = 0;
-	spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes),
-	spec->channel_mode = cxt5045_modes,
+	spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes);
+	spec->channel_mode = cxt5045_modes;
 
+	set_beep_amp(spec, 0x16, 0, 1);
 
 	codec->patch_ops = conexant_patch_ops;
 
@@ -1211,6 +1259,9 @@
 		break;
 	}
 
+	if (spec->beep_amp)
+		snd_hda_attach_beep_device(codec, spec->beep_amp);
+
 	return 0;
 }
 
@@ -1632,6 +1683,11 @@
 	pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			    pinctl);
+	/* on ideapad there is an aditional speaker (subwoofer) to mute */
+	if (spec->ideapad)
+		snd_hda_codec_write(codec, 0x1b, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -1888,6 +1944,13 @@
 #endif
 }
 
+static struct hda_verb cxt5051_ideapad_init_verbs[] = {
+	/* Subwoofer */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{ } /* end */
+};
+
 /* initialize jack-sensing, too */
 static int cxt5051_init(struct hda_codec *codec)
 {
@@ -1917,6 +1980,7 @@
 	CXT5051_LENOVO_X200,	/* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
 	CXT5051_F700,       /* HP Compaq Presario F700 */
 	CXT5051_TOSHIBA,	/* Toshiba M300 & co */
+	CXT5051_IDEAPAD,	/* Lenovo IdeaPad Y430 */
 	CXT5051_MODELS
 };
 
@@ -1927,6 +1991,7 @@
 	[CXT5051_LENOVO_X200]	= "lenovo-x200",
 	[CXT5051_F700]          = "hp-700",
 	[CXT5051_TOSHIBA]	= "toshiba",
+	[CXT5051_IDEAPAD]	= "ideapad",
 };
 
 static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
@@ -1938,6 +2003,7 @@
 		      CXT5051_LAPTOP),
 	SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
 	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
+	SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD),
 	{}
 };
 
@@ -1972,6 +2038,8 @@
 	spec->cur_adc = 0;
 	spec->cur_adc_idx = 0;
 
+	set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
+
 	codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
 
 	board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
@@ -1989,6 +2057,10 @@
 		break;
 	case CXT5051_LENOVO_X200:
 		spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
+		/* Thinkpad X301 does not have S/PDIF wired and no ability
+		   to use a docking station. */
+		if (codec->subsystem_id == 0x17aa211f)
+			spec->multiout.dig_out_nid = 0;
 		break;
 	case CXT5051_F700:
 		spec->init_verbs[0] = cxt5051_f700_init_verbs;
@@ -1999,8 +2071,16 @@
 		spec->mixers[0] = cxt5051_toshiba_mixers;
 		spec->auto_mic = AUTO_MIC_PORTB;
 		break;
+	case CXT5051_IDEAPAD:
+		spec->init_verbs[spec->num_init_verbs++] =
+			cxt5051_ideapad_init_verbs;
+		spec->ideapad = 1;
+		break;
 	}
 
+	if (spec->beep_amp)
+		snd_hda_attach_beep_device(codec, spec->beep_amp);
+
 	return 0;
 }
 
@@ -2616,7 +2696,6 @@
 		.put = cxt5066_mic_boost_mux_enum_put,
 		.private_value = 0x23 | 0x100,
 	},
-	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
 	{}
 };
 
@@ -2977,8 +3056,10 @@
 	SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
+ 	SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
+ 	SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD),
+	SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
-	SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
 	{}
 };
 
@@ -3014,6 +3095,8 @@
 	spec->cur_adc = 0;
 	spec->cur_adc_idx = 0;
 
+	set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
+
 	board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
 						  cxt5066_models, cxt5066_cfg_tbl);
 	switch (board_config) {
@@ -3062,7 +3145,6 @@
 		spec->port_d_mode = 0;
 		spec->dell_vostro = 1;
 		spec->mic_boost = 3; /* default 30dB gain */
-		snd_hda_attach_beep_device(codec, 0x13);
 
 		/* no S/PDIF out */
 		spec->multiout.dig_out_nid = 0;
@@ -3104,6 +3186,9 @@
 		break;
 	}
 
+	if (spec->beep_amp)
+		snd_hda_attach_beep_device(codec, spec->beep_amp);
+
 	return 0;
 }
 
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 2fc5396..522e074 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -698,11 +698,51 @@
  * Callbacks
  */
 
-static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+/* HBR should be Non-PCM, 8 channels */
+#define is_hbr_format(format) \
+	((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
+
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 			      u32 stream_tag, int format)
 {
+	struct hdmi_spec *spec = codec->spec;
 	int tag;
 	int fmt;
+	int pinctl;
+	int new_pinctl = 0;
+	int i;
+
+	for (i = 0; i < spec->num_pins; i++) {
+		if (spec->pin_cvt[i] != nid)
+			continue;
+		if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR))
+			continue;
+
+		pinctl = snd_hda_codec_read(codec, spec->pin[i], 0,
+					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+		new_pinctl = pinctl & ~AC_PINCTL_EPT;
+		if (is_hbr_format(format))
+			new_pinctl |= AC_PINCTL_EPT_HBR;
+		else
+			new_pinctl |= AC_PINCTL_EPT_NATIVE;
+
+		snd_printdd("hdmi_setup_stream: "
+			    "NID=0x%x, %spinctl=0x%x\n",
+			    spec->pin[i],
+			    pinctl == new_pinctl ? "" : "new-",
+			    new_pinctl);
+
+		if (pinctl != new_pinctl)
+			snd_hda_codec_write(codec, spec->pin[i], 0,
+					    AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    new_pinctl);
+	}
+
+	if (is_hbr_format(format) && !new_pinctl) {
+		snd_printdd("hdmi_setup_stream: HBR is not supported\n");
+		return -EINVAL;
+	}
 
 	tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
 	fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
@@ -722,6 +762,7 @@
 	if (fmt != format)
 		snd_hda_codec_write(codec, nid, 0,
 				    AC_VERB_SET_STREAM_FORMAT, format);
+	return 0;
 }
 
 /*
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
index b81d23e..5972d5e 100644
--- a/sound/pci/hda/patch_intelhdmi.c
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -66,8 +66,7 @@
 
 	hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
 
-	hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-	return 0;
+	return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
 }
 
 static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index b0652ac..a281836 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -202,8 +202,7 @@
 
 	hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
 
-	hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-	return 0;
+	return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
 }
 
 static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 596ea2f..6ac53f7 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -256,6 +256,13 @@
 	ALC882_MODEL_LAST,
 };
 
+/* ALC680 models */
+enum {
+	ALC680_BASE,
+	ALC680_AUTO,
+	ALC680_MODEL_LAST,
+};
+
 /* for GPIO Poll */
 #define GPIO_MASK	0x03
 
@@ -326,6 +333,12 @@
 	hda_nid_t *capsrc_nids;
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
 
+	/* capture setup for dynamic dual-adc switch */
+	unsigned int cur_adc_idx;
+	hda_nid_t cur_adc;
+	unsigned int cur_adc_stream_tag;
+	unsigned int cur_adc_format;
+
 	/* capture source */
 	unsigned int num_mux_defs;
 	const struct hda_input_mux *input_mux;
@@ -367,6 +380,7 @@
 
 	/* other flags */
 	unsigned int no_analog :1; /* digital I/O only */
+	unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
 	int init_amp;
 
 	/* for virtual master */
@@ -833,9 +847,13 @@
 
 	if (auto_pin_type <= AUTO_PIN_FRONT_MIC) {
 		unsigned int pincap;
+		unsigned int oldval;
+		oldval = snd_hda_codec_read(codec, nid, 0,
+					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		pincap = snd_hda_query_pin_caps(codec, nid);
 		pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-		if (pincap & AC_PINCAP_VREF_80)
+		/* if the default pin setup is vref50, we give it priority */
+		if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
 			val = PIN_VREF80;
 		else if (pincap & AC_PINCAP_VREF_50)
 			val = PIN_VREF50;
@@ -1003,6 +1021,29 @@
 	return -1;
 }
 
+/* switch the current ADC according to the jack state */
+static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int present;
+	hda_nid_t new_adc;
+
+	present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
+	if (present)
+		spec->cur_adc_idx = 1;
+	else
+		spec->cur_adc_idx = 0;
+	new_adc = spec->adc_nids[spec->cur_adc_idx];
+	if (spec->cur_adc && spec->cur_adc != new_adc) {
+		/* stream is running, let's swap the current ADC */
+		snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+		spec->cur_adc = new_adc;
+		snd_hda_codec_setup_stream(codec, new_adc,
+					   spec->cur_adc_stream_tag, 0,
+					   spec->cur_adc_format);
+	}
+}
+
 static void alc_mic_automute(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -1017,6 +1058,11 @@
 	if (snd_BUG_ON(!spec->adc_nids))
 		return;
 
+	if (spec->dual_adc_switch) {
+		alc_dual_mic_adc_auto_switch(codec);
+		return;
+	}
+
 	cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
 
 	present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
@@ -1499,6 +1545,63 @@
 	return val;
 }
 
+/* set right pin controls for digital I/O */
+static void alc_auto_init_digital(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+	hda_nid_t pin;
+
+	for (i = 0; i < spec->autocfg.dig_outs; i++) {
+		pin = spec->autocfg.dig_out_pins[i];
+		if (pin) {
+			snd_hda_codec_write(codec, pin, 0,
+					    AC_VERB_SET_PIN_WIDGET_CONTROL,
+					    PIN_OUT);
+		}
+	}
+	pin = spec->autocfg.dig_in_pin;
+	if (pin)
+		snd_hda_codec_write(codec, pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    PIN_IN);
+}
+
+/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
+static void alc_auto_parse_digital(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i, err;
+	hda_nid_t dig_nid;
+
+	/* support multiple SPDIFs; the secondary is set up as a slave */
+	for (i = 0; i < spec->autocfg.dig_outs; i++) {
+		err = snd_hda_get_connections(codec,
+					      spec->autocfg.dig_out_pins[i],
+					      &dig_nid, 1);
+		if (err < 0)
+			continue;
+		if (!i) {
+			spec->multiout.dig_out_nid = dig_nid;
+			spec->dig_out_type = spec->autocfg.dig_out_type[0];
+		} else {
+			spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+			if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
+				break;
+			spec->slave_dig_outs[i - 1] = dig_nid;
+		}
+	}
+
+	if (spec->autocfg.dig_in_pin) {
+		hda_nid_t dig_nid;
+		err = snd_hda_get_connections(codec,
+					      spec->autocfg.dig_in_pin,
+					      &dig_nid, 1);
+		if (err > 0)
+			spec->dig_in_nid = dig_nid;
+	}
+}
+
 /*
  * ALC888
  */
@@ -3607,6 +3710,41 @@
 	return 0;
 }
 
+/* analog capture with dynamic dual-adc changes */
+static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+	spec->cur_adc_stream_tag = stream_tag;
+	spec->cur_adc_format = format;
+	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+	return 0;
+}
+
+static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+	spec->cur_adc = 0;
+	return 0;
+}
+
+static struct hda_pcm_stream dualmic_pcm_analog_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0, /* fill later */
+	.ops = {
+		.prepare = dualmic_capture_pcm_prepare,
+		.cleanup = dualmic_capture_pcm_cleanup
+	},
+};
 
 /*
  */
@@ -4936,7 +5074,7 @@
 static int alc880_parse_auto_config(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	int i, err;
+	int err;
 	static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
 
 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
@@ -4967,25 +5105,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	/* check multiple SPDIF-out (for recent codecs) */
-	for (i = 0; i < spec->autocfg.dig_outs; i++) {
-		hda_nid_t dig_nid;
-		err = snd_hda_get_connections(codec,
-					      spec->autocfg.dig_out_pins[i],
-					      &dig_nid, 1);
-		if (err < 0)
-			continue;
-		if (!i)
-			spec->multiout.dig_out_nid = dig_nid;
-		else {
-			spec->multiout.slave_dig_outs = spec->slave_dig_outs;
-			if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
-				break;
-			spec->slave_dig_outs[i - 1] = dig_nid;
-		}
-	}
-	if (spec->autocfg.dig_in_pin)
-		spec->dig_in_nid = ALC880_DIGIN_NID;
+	alc_auto_parse_digital(codec);
 
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
@@ -5008,6 +5128,7 @@
 	alc880_auto_init_extra_out(codec);
 	alc880_auto_init_analog_input(codec);
 	alc880_auto_init_input_src(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -5045,6 +5166,39 @@
 	spec->auto_mic = 0; /* disable auto-mic to be sure */
 }
 
+/* select or unmute the given capsrc route */
+static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
+				    int idx)
+{
+	if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
+		snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
+					 HDA_AMP_MUTE, 0);
+	} else {
+		snd_hda_codec_write_cache(codec, cap, 0,
+					  AC_VERB_SET_CONNECT_SEL, idx);
+	}
+}
+
+/* set the default connection to that pin */
+static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->num_adc_nids; i++) {
+		hda_nid_t cap = spec->capsrc_nids ?
+			spec->capsrc_nids[i] : spec->adc_nids[i];
+		int idx;
+
+		idx = get_connection_index(codec, cap, pin);
+		if (idx < 0)
+			continue;
+		select_or_unmute_capsrc(codec, cap, idx);
+		return i; /* return the found index */
+	}
+	return -1; /* not found */
+}
+
 /* choose the ADC/MUX containing the input pin and initialize the setup */
 static void fixup_single_adc(struct hda_codec *codec)
 {
@@ -5061,33 +5215,24 @@
 	}
 	if (!pin)
 		return;
-
-	/* set the default connection to that pin */
-	for (i = 0; i < spec->num_adc_nids; i++) {
-		hda_nid_t cap = spec->capsrc_nids ?
-			spec->capsrc_nids[i] : spec->adc_nids[i];
-		int idx;
-
-		idx = get_connection_index(codec, cap, pin);
-		if (idx < 0)
-			continue;
+	i = init_capsrc_for_pin(codec, pin);
+	if (i >= 0) {
 		/* use only this ADC */
 		if (spec->capsrc_nids)
 			spec->capsrc_nids += i;
 		spec->adc_nids += i;
 		spec->num_adc_nids = 1;
-		/* select or unmute this route */
-		if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
-			snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
-						 HDA_AMP_MUTE, 0);
-		} else {
-			snd_hda_codec_write_cache(codec, cap, 0,
-					  AC_VERB_SET_CONNECT_SEL, idx);
-		}
-		return;
 	}
 }
 
+/* initialize dual adcs */
+static void fixup_dual_adc_switch(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	init_capsrc_for_pin(codec, spec->ext_mic.pin);
+	init_capsrc_for_pin(codec, spec->int_mic.pin);
+}
+
 static void set_capture_mixer(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -5101,7 +5246,10 @@
 	};
 	if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
 		int mux = 0;
-		if (spec->auto_mic)
+		int num_adcs = spec->num_adc_nids;
+		if (spec->dual_adc_switch)
+			fixup_dual_adc_switch(codec);
+		else if (spec->auto_mic)
 			fixup_automic_adc(codec);
 		else if (spec->input_mux) {
 			if (spec->input_mux->num_items > 1)
@@ -5109,7 +5257,9 @@
 			else if (spec->input_mux->num_items == 1)
 				fixup_single_adc(codec);
 		}
-		spec->cap_mixer = caps[mux][spec->num_adc_nids - 1];
+		if (spec->dual_adc_switch)
+			num_adcs = 1;
+		spec->cap_mixer = caps[mux][num_adcs - 1];
 	}
 }
 
@@ -5183,6 +5333,7 @@
 
 static struct snd_pci_quirk beep_white_list[] = {
 	SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
+	SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
 	{}
 };
 
@@ -6624,6 +6775,7 @@
 	alc260_auto_init_multi_out(codec);
 	alc260_auto_init_analog_input(codec);
 	alc260_auto_init_input_src(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -6640,6 +6792,29 @@
 #endif
 
 /*
+ * Pin config fixes
+ */
+enum {
+	PINFIX_HP_DC5750,
+};
+
+static struct alc_pincfg alc260_hp_dc5750_pinfix[] = {
+	{ 0x11, 0x90130110 }, /* speaker */
+	{ }
+};
+
+static const struct alc_fixup alc260_fixups[] = {
+	[PINFIX_HP_DC5750] = {
+		.pins = alc260_hp_dc5750_pinfix
+	},
+};
+
+static struct snd_pci_quirk alc260_fixup_tbl[] = {
+	SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750),
+	{}
+};
+
+/*
  * ALC260 configurations
  */
 static const char *alc260_models[ALC260_MODEL_LAST] = {
@@ -6838,6 +7013,9 @@
 		board_config = ALC260_AUTO;
 	}
 
+	if (board_config == ALC260_AUTO)
+		alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 1);
+
 	if (board_config == ALC260_AUTO) {
 		/* automatic parse from the BIOS config */
 		err = alc260_parse_auto_config(codec);
@@ -6883,6 +7061,9 @@
 	set_capture_mixer(codec);
 	set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
 
+	if (board_config == ALC260_AUTO)
+		alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 0);
+
 	spec->vmaster_nid = 0x08;
 
 	codec->patch_ops = alc_patch_ops;
@@ -7003,7 +7184,7 @@
 	.num_items = 4,
 	.items = {
 		{ "Mic", 0x0 },
-		{ "iMic", 0x1 },
+		{ "Int Mic", 0x1 },
 		{ "Line", 0x2 },
 		{ "CD", 0x4 },
 	},
@@ -8573,8 +8754,8 @@
 	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	{ } /* end */
 };
 
@@ -10265,7 +10446,8 @@
  * Pin config fixes
  */
 enum {
-	PINFIX_ABIT_AW9D_MAX
+	PINFIX_ABIT_AW9D_MAX,
+	PINFIX_PB_M5210,
 };
 
 static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
@@ -10275,13 +10457,22 @@
 	{ }
 };
 
+static const struct hda_verb pb_m5210_verbs[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+	{}
+};
+
 static const struct alc_fixup alc882_fixups[] = {
 	[PINFIX_ABIT_AW9D_MAX] = {
 		.pins = alc882_abit_aw9d_pinfix
 	},
+	[PINFIX_PB_M5210] = {
+		.verbs = pb_m5210_verbs
+	},
 };
 
 static struct snd_pci_quirk alc882_fixup_tbl[] = {
+	SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
 	SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
 	{}
 };
@@ -10446,7 +10637,7 @@
 {
 	struct alc_spec *spec = codec->spec;
 	static hda_nid_t alc882_ignore[] = { 0x1d, 0 };
-	int i, err;
+	int err;
 
 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
 					   alc882_ignore);
@@ -10476,25 +10667,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	/* check multiple SPDIF-out (for recent codecs) */
-	for (i = 0; i < spec->autocfg.dig_outs; i++) {
-		hda_nid_t dig_nid;
-		err = snd_hda_get_connections(codec,
-					      spec->autocfg.dig_out_pins[i],
-					      &dig_nid, 1);
-		if (err < 0)
-			continue;
-		if (!i)
-			spec->multiout.dig_out_nid = dig_nid;
-		else {
-			spec->multiout.slave_dig_outs = spec->slave_dig_outs;
-			if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
-				break;
-			spec->slave_dig_outs[i - 1] = dig_nid;
-		}
-	}
-	if (spec->autocfg.dig_in_pin)
-		spec->dig_in_nid = ALC880_DIGIN_NID;
+	alc_auto_parse_digital(codec);
 
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
@@ -10524,6 +10697,7 @@
 	alc882_auto_init_hp_out(codec);
 	alc882_auto_init_analog_input(codec);
 	alc882_auto_init_input_src(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -12054,12 +12228,7 @@
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
  dig_only:
-	if (spec->autocfg.dig_outs) {
-		spec->multiout.dig_out_nid = ALC262_DIGOUT_NID;
-		spec->dig_out_type = spec->autocfg.dig_out_type[0];
-	}
-	if (spec->autocfg.dig_in_pin)
-		spec->dig_in_nid = ALC262_DIGIN_NID;
+	alc_auto_parse_digital(codec);
 
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
@@ -12091,6 +12260,7 @@
 	alc262_auto_init_hp_out(codec);
 	alc262_auto_init_analog_input(codec);
 	alc262_auto_init_input_src(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -13024,10 +13194,14 @@
 		dac = 0x02;
 		break;
 	case 0x15:
+	case 0x1a: /* ALC259/269 only */
+	case 0x1b: /* ALC259/269 only */
 	case 0x21: /* ALC269vb has this pin, too */
 		dac = 0x03;
 		break;
 	default:
+		snd_printd(KERN_WARNING "hda_codec: "
+			   "ignoring pin 0x%x as unknown\n", nid);
 		return 0;
 	}
 	if (spec->multiout.dac_nids[0] != dac &&
@@ -13078,7 +13252,7 @@
 				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
 		if (err < 0)
 			return err;
-	} else {
+	} else if (nid) {
 		err = alc268_new_analog_output(spec, nid, "Speaker", 0);
 		if (err < 0)
 			return err;
@@ -13227,10 +13401,7 @@
 
  dig_only:
 	/* digital only support output */
-	if (spec->autocfg.dig_outs) {
-		spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
-		spec->dig_out_type = spec->autocfg.dig_out_type[0];
-	}
+	alc_auto_parse_digital(codec);
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
@@ -13260,6 +13431,7 @@
 	alc268_auto_init_hp_out(codec);
 	alc268_auto_init_mono_speaker_out(codec);
 	alc268_auto_init_analog_input(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -14152,6 +14324,36 @@
 }
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
+static int alc275_setup_dual_adc(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
+		return 0;
+	if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
+	    (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
+		if (spec->ext_mic.pin <= 0x12) {
+			spec->private_adc_nids[0] = 0x08;
+			spec->private_adc_nids[1] = 0x11;
+			spec->private_capsrc_nids[0] = 0x23;
+			spec->private_capsrc_nids[1] = 0x22;
+		} else {
+			spec->private_adc_nids[0] = 0x11;
+			spec->private_adc_nids[1] = 0x08;
+			spec->private_capsrc_nids[0] = 0x22;
+			spec->private_capsrc_nids[1] = 0x23;
+		}
+		spec->adc_nids = spec->private_adc_nids;
+		spec->capsrc_nids = spec->private_capsrc_nids;
+		spec->num_adc_nids = 2;
+		spec->dual_adc_switch = 1;
+		snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
+			    spec->adc_nids[0], spec->adc_nids[1]);
+		return 1;
+	}
+	return 0;
+}
+
 /*
  * BIOS auto configuration
  */
@@ -14175,8 +14377,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	if (spec->autocfg.dig_outs)
-		spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
+	alc_auto_parse_digital(codec);
 
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
@@ -14191,13 +14392,15 @@
 
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
-	fillup_priv_adc_nids(codec, alc269_adc_candidates,
-			     sizeof(alc269_adc_candidates));
+
+	if (!alc275_setup_dual_adc(codec))
+		fillup_priv_adc_nids(codec, alc269_adc_candidates,
+				     sizeof(alc269_adc_candidates));
 
 	/* set default input source */
-	snd_hda_codec_write_cache(codec, spec->capsrc_nids[0],
-				  0, AC_VERB_SET_CONNECT_SEL,
-				  spec->input_mux->items[0].index);
+	if (!spec->dual_adc_switch)
+		select_or_unmute_capsrc(codec, spec->capsrc_nids[0],
+					spec->input_mux->items[0].index);
 
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
@@ -14221,6 +14424,7 @@
 	alc269_auto_init_multi_out(codec);
 	alc269_auto_init_hp_out(codec);
 	alc269_auto_init_analog_input(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -14493,6 +14697,10 @@
 		 */
 		spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
 		spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
+	} else if (spec->dual_adc_switch) {
+		spec->stream_analog_playback = &alc269_pcm_analog_playback;
+		/* switch ADC dynamically */
+		spec->stream_analog_capture = &dualmic_pcm_analog_capture;
 	} else {
 		spec->stream_analog_playback = &alc269_pcm_analog_playback;
 		spec->stream_analog_capture = &alc269_pcm_analog_capture;
@@ -15378,8 +15586,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	if (spec->autocfg.dig_outs)
-		spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
+	alc_auto_parse_digital(codec);
 
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
@@ -15405,6 +15612,7 @@
 	alc861_auto_init_multi_out(codec);
 	alc861_auto_init_hp_out(codec);
 	alc861_auto_init_analog_input(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -16509,8 +16717,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	if (spec->autocfg.dig_outs)
-		spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
+	alc_auto_parse_digital(codec);
 
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
@@ -16537,6 +16744,7 @@
 	alc861vd_auto_init_hp_out(codec);
 	alc861vd_auto_init_analog_input(codec);
 	alc861vd_auto_init_input_src(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -18520,7 +18728,7 @@
 					      hda_nid_t dac)
 {
 	int i, num;
-	hda_nid_t srcs[4];
+	hda_nid_t srcs[HDA_MAX_CONNECTIONS];
 
 	alc_set_pin_output(codec, nid, pin_type);
 	/* need the manual connection? */
@@ -18624,8 +18832,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	if (spec->autocfg.dig_outs)
-		spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+	alc_auto_parse_digital(codec);
 
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
@@ -18635,7 +18842,7 @@
 
 	add_verb(spec, alc662_init_verbs);
 	if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
-	    codec->vendor_id == 0x10ec0665)
+	    codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
 		add_verb(spec, alc663_init_verbs);
 
 	if (codec->vendor_id == 0x10ec0272)
@@ -18662,6 +18869,7 @@
 	alc662_auto_init_hp_out(codec);
 	alc662_auto_init_analog_input(codec);
 	alc662_auto_init_input_src(codec);
+	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
 }
@@ -18781,6 +18989,333 @@
 }
 
 /*
+ * ALC680 support
+ */
+#define ALC680_DIGOUT_NID	ALC880_DIGOUT_NID
+#define alc680_modes		alc260_modes
+
+static hda_nid_t alc680_dac_nids[3] = {
+	/* Lout1, Lout2, hp */
+	0x02, 0x03, 0x04
+};
+
+static hda_nid_t alc680_adc_nids[3] = {
+	/* ADC0-2 */
+	/* DMIC, MIC, Line-in*/
+	0x07, 0x08, 0x09
+};
+
+static struct snd_kcontrol_new alc680_base_mixer[] = {
+	/* output mixer control */
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	{ }
+};
+
+static struct snd_kcontrol_new alc680_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+	{ } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc680_init_verbs[] = {
+	/* Unmute DAC0-1 and set vol = 0 */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{ }
+};
+
+/* create input playback/capture controls for the given pin */
+static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+				    const char *ctlname, int idx)
+{
+	hda_nid_t dac;
+	int err;
+
+	switch (nid) {
+	case 0x14:
+		dac = 0x02;
+		break;
+	case 0x15:
+		dac = 0x03;
+		break;
+	case 0x16:
+		dac = 0x04;
+		break;
+	default:
+		return 0;
+	}
+	if (spec->multiout.dac_nids[0] != dac &&
+	    spec->multiout.dac_nids[1] != dac) {
+		err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+				  HDA_COMPOSE_AMP_VAL(dac, 3, idx,
+						      HDA_OUTPUT));
+		if (err < 0)
+			return err;
+
+		err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+			  HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+
+		if (err < 0)
+			return err;
+		spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+	}
+
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	hda_nid_t nid;
+	int err;
+
+	spec->multiout.dac_nids = spec->private_dac_nids;
+
+	nid = cfg->line_out_pins[0];
+	if (nid) {
+		const char *name;
+		if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+			name = "Speaker";
+		else
+			name = "Front";
+		err = alc680_new_analog_output(spec, nid, name, 0);
+		if (err < 0)
+			return err;
+	}
+
+	nid = cfg->speaker_pins[0];
+	if (nid) {
+		err = alc680_new_analog_output(spec, nid, "Speaker", 0);
+		if (err < 0)
+			return err;
+	}
+	nid = cfg->hp_pins[0];
+	if (nid) {
+		err = alc680_new_analog_output(spec, nid, "Headphone", 0);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static void alc680_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type)
+{
+	alc_set_pin_output(codec, nid, pin_type);
+}
+
+static void alc680_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t nid = spec->autocfg.line_out_pins[0];
+	if (nid) {
+		int pin_type = get_pin_type(spec->autocfg.line_out_type);
+		alc680_auto_set_output_and_unmute(codec, nid, pin_type);
+	}
+}
+
+static void alc680_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.hp_pins[0];
+	if (pin)
+		alc680_auto_set_output_and_unmute(codec, pin, PIN_HP);
+	pin = spec->autocfg.speaker_pins[0];
+	if (pin)
+		alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+}
+
+/* pcm configuration: identical with ALC880 */
+#define alc680_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc680_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc680_pcm_analog_alt_capture	alc880_pcm_analog_alt_capture
+#define alc680_pcm_digital_playback	alc880_pcm_digital_playback
+
+static struct hda_input_mux alc680_capture_source = {
+	.num_items = 1,
+	.items = {
+		{ "Mic", 0x0 },
+	},
+};
+
+/*
+ * BIOS auto configuration
+ */
+static int alc680_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t alc680_ignore[] = { 0 };
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+					   alc680_ignore);
+	if (err < 0)
+		return err;
+	if (!spec->autocfg.line_outs) {
+		if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
+			spec->multiout.max_channels = 2;
+			spec->no_analog = 1;
+			goto dig_only;
+		}
+		return 0; /* can't find valid BIOS pin config */
+	}
+	err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+
+	spec->multiout.max_channels = 2;
+
+ dig_only:
+	/* digital only support output */
+	alc_auto_parse_digital(codec);
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
+
+	add_verb(spec, alc680_init_verbs);
+	spec->num_mux_defs = 1;
+	spec->input_mux = &alc680_capture_source;
+
+	err = alc_auto_add_mic_boost(codec);
+	if (err < 0)
+		return err;
+
+	return 1;
+}
+
+#define alc680_auto_init_analog_input	alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc680_auto_init(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	alc680_auto_init_multi_out(codec);
+	alc680_auto_init_hp_out(codec);
+	alc680_auto_init_analog_input(codec);
+	alc_auto_init_digital(codec);
+	if (spec->unsol_event)
+		alc_inithook(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc680_models[ALC680_MODEL_LAST] = {
+	[ALC680_BASE]		= "base",
+	[ALC680_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc680_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE),
+	{}
+};
+
+static struct alc_config_preset alc680_presets[] = {
+	[ALC680_BASE] = {
+		.mixers = { alc680_base_mixer },
+		.cap_mixer =  alc680_capture_mixer,
+		.init_verbs = { alc680_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc680_dac_nids),
+		.dac_nids = alc680_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc680_adc_nids),
+		.adc_nids = alc680_adc_nids,
+		.hp_nid = 0x04,
+		.dig_out_nid = ALC680_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc680_modes),
+		.channel_mode = alc680_modes,
+		.input_mux = &alc680_capture_source,
+	},
+};
+
+static int patch_alc680(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int board_config;
+	int err;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
+						  alc680_models,
+						  alc680_cfg_tbl);
+
+	if (board_config < 0 || board_config >= ALC680_MODEL_LAST) {
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
+		board_config = ALC680_AUTO;
+	}
+
+	if (board_config == ALC680_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc680_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (!err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC680_BASE;
+		}
+	}
+
+	if (board_config != ALC680_AUTO)
+		setup_preset(codec, &alc680_presets[board_config]);
+
+	spec->stream_analog_playback = &alc680_pcm_analog_playback;
+	spec->stream_analog_capture = &alc680_pcm_analog_capture;
+	spec->stream_analog_alt_capture = &alc680_pcm_analog_alt_capture;
+	spec->stream_digital_playback = &alc680_pcm_digital_playback;
+
+	if (!spec->adc_nids) {
+		spec->adc_nids = alc680_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids);
+	}
+
+	if (!spec->cap_mixer)
+		set_capture_mixer(codec);
+
+	spec->vmaster_nid = 0x02;
+
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC680_AUTO)
+		spec->init_hook = alc680_auto_init;
+
+	return 0;
+}
+
+/*
  * patch entries
  */
 static struct hda_codec_preset snd_hda_preset_realtek[] = {
@@ -18804,6 +19339,7 @@
 	{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
 	{ .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
 	{ .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
+	{ .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
 	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index f1e7bab..b8d730c 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -202,6 +202,7 @@
 	unsigned int spdif_mute: 1;
 	unsigned int check_volume_offset:1;
 	unsigned int auto_mic:1;
+	unsigned int linear_tone_beep:1;
 
 	/* gpio lines */
 	unsigned int eapd_mask;
@@ -3802,7 +3803,7 @@
 			return err;
 		if (codec->beep) {
 			/* IDT/STAC codecs have linear beep tone parameter */
-			codec->beep->linear_tone = 1;
+			codec->beep->linear_tone = spec->linear_tone_beep;
 			/* if no beep switch is available, make its own one */
 			caps = query_amp_caps(codec, nid, HDA_OUTPUT);
 			if (!(caps & AC_AMPCAP_MUTE)) {
@@ -5005,6 +5006,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 1;
 	spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
 	spec->pin_nids = stac9200_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
@@ -5068,6 +5070,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 1;
 	spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
 	spec->pin_nids = stac925x_pin_nids;
 
@@ -5153,6 +5156,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 0;
 	codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;
 	spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
 	spec->pin_nids = stac92hd73xx_pin_nids;
@@ -5300,6 +5304,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 1;
 	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
 	spec->digbeep_nid = 0x21;
 	spec->mux_nids = stac92hd83xxx_mux_nids;
@@ -5522,6 +5527,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 0;
 	codec->patch_ops = stac92xx_patch_ops;
 	spec->num_pins = STAC92HD71BXX_NUM_PINS;
 	switch (codec->vendor_id) {
@@ -5779,6 +5785,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 1;
 	spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
 	spec->pin_nids = stac922x_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
@@ -5883,6 +5890,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 1;
 	codec->slave_dig_outs = stac927x_slave_dig_outs;
 	spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
 	spec->pin_nids = stac927x_pin_nids;
@@ -6018,6 +6026,7 @@
 
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 1;
 	spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
 	spec->pin_nids = stac9205_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
@@ -6174,6 +6183,7 @@
 		return -ENOMEM;
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
+	spec->linear_tone_beep = 1;
 	spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
 	spec->pin_nids = stac9872_pin_nids;
 
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 7345381..ae3acb2 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -552,24 +552,30 @@
 	}
 }
 
+static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
+
 static void via_auto_init_analog_input(struct hda_codec *codec)
 {
 	struct via_spec *spec = codec->spec;
+	unsigned int ctl;
 	int i;
 
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = spec->autocfg.input_pins[i];
+		if (!nid)
+			continue;
 
+		if (spec->smart51_enabled && is_smart51_pins(spec, nid))
+			ctl = PIN_OUT;
+		else if (i <= AUTO_PIN_FRONT_MIC)
+			ctl = PIN_VREF50;
+		else
+			ctl = PIN_IN;
 		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    (i <= AUTO_PIN_FRONT_MIC ?
-				     PIN_VREF50 : PIN_IN));
-
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
 	}
 }
 
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
-
 static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
 				unsigned int *affected_parm)
 {
@@ -658,6 +664,8 @@
 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
 		parm = AC_PWRST_D3;
 		set_pin_power_state(codec, 0x19, &parm);
+		if (spec->smart51_enabled)
+			parm = AC_PWRST_D0;
 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
 				    parm);
 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
@@ -667,6 +675,8 @@
 		if (is_8ch) {
 			parm = AC_PWRST_D3;
 			set_pin_power_state(codec, 0x22, &parm);
+			if (spec->smart51_enabled)
+				parm = AC_PWRST_D0;
 			snd_hda_codec_write(codec, 0x26, 0,
 					    AC_VERB_SET_POWER_STATE, parm);
 			snd_hda_codec_write(codec, 0x24, 0,
@@ -3915,6 +3925,13 @@
 		}
 	}
 
+	/* for Smart 5.1, line/mic inputs double as output pins */
+	if (cfg->line_outs == 1) {
+		spec->multiout.num_dacs = 3;
+		spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
+		spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
+	}
+
 	return 0;
 }
 
@@ -3932,7 +3949,8 @@
 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
 		nid = cfg->line_out_pins[i];
 
-		if (!nid)
+		/* for Smart 5.1, there are always at least six channels */
+		if (!nid && i > AUTO_SEQ_CENLFE)
 			continue;
 
 		nid_vol = nid_vols[i];
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index ad44626..f64fb7d 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -97,6 +97,7 @@
 #include <linux/gameport.h>
 #include <linux/device.h>
 #include <linux/firmware.h>
+#include <linux/kernel.h>
 #include <asm/io.h>
 #include <sound/core.h>
 #include <sound/info.h>
@@ -667,13 +668,12 @@
 	unsigned char c;
 
 	while (len) {
+		int value;
+
 		c = in[len - 1];
-		if ((c >= '0') && (c <= '9'))
-			sum += mult * (c - '0');
-		else if ((c >= 'A') && (c <= 'F'))
-			sum += mult * (c - ('A' - 10));
-		else if ((c >= 'a') && (c <= 'f'))
-			sum += mult * (c - ('a' - 10));
+		value = hex_to_bin(c);
+		if (value >= 0)
+			sum += mult * value;
 		mult *= 16;
 		--len;
 	}
@@ -1615,7 +1615,10 @@
 
 	chip->playback_substream[sub_num] = substream;
 	runtime->hw = snd_riptide_playback;
+
 	data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
 	data->paths = lbus_play_paths[sub_num];
 	data->id = play_ids[sub_num];
 	data->source = play_sources[sub_num];
@@ -1635,7 +1638,10 @@
 
 	chip->capture_substream = substream;
 	runtime->hw = snd_riptide_capture;
+
 	data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
 	data->paths = lbus_rec_path;
 	data->id = PADC;
 	data->source = ACLNK2PADC;
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 9cc1b5a..1b8f674 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -264,11 +264,13 @@
 		 * if using small periods.
 		 *
 		 * If we're less than 9 samples behind, we're on target.
+		 * Otherwise, shorten the next vperiod by the amount we've
+		 * been delayed.
 		 */
 		if (sync > -9)
 			voice->vperiod = voice->sync_period_size + 1;
 		else
-			voice->vperiod = voice->sync_period_size - 4;
+			voice->vperiod = voice->sync_period_size + sync + 10;
 
 		if (voice->vperiod < voice->buffer_size) {
 			sis_update_sso(voice, voice->vperiod);
@@ -736,7 +738,7 @@
 	period_size = buffer_size;
 
 	/* Initially, we want to interrupt just a bit behind the end of
-	 * the period we're clocking out. 10 samples seems to give a good
+	 * the period we're clocking out. 12 samples seems to give a good
 	 * delay.
 	 *
 	 * We want to spread our interrupts throughout the virtual period,
@@ -747,7 +749,7 @@
 	 *
 	 * This is all moot if we don't need to use virtual periods.
 	 */
-	vperiod = runtime->period_size + 10;
+	vperiod = runtime->period_size + 12;
 	if (vperiod > period_size) {
 		u16 tail = vperiod % period_size;
 		u16 quarter_period = period_size / 4;
@@ -776,7 +778,7 @@
 	 */
 	timing->flags |= VOICE_SYNC_TIMING;
 	timing->sync_base = voice->ctrl_base;
-	timing->sync_cso = runtime->period_size - 1;
+	timing->sync_cso = runtime->period_size;
 	timing->sync_period_size = runtime->period_size;
 	timing->sync_buffer_size = runtime->buffer_size;
 	timing->period_size = period_size;
@@ -1047,7 +1049,7 @@
 	/* Reset the chip, and disable all interrputs.
 	 */
 	outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR);
-	udelay(10);
+	udelay(25);
 	outl(0, sis->ioport + SIS_GCR);
 	outl(0, sis->ioport + SIS_GIER);
 
@@ -1083,7 +1085,7 @@
 	/* Reset the audio controller
 	 */
 	outl(SIS_GCR_SOFTWARE_RESET, io + SIS_GCR);
-	udelay(10);
+	udelay(25);
 	outl(0, io + SIS_GCR);
 
 	/* Get the AC-link semaphore, and reset the codecs
@@ -1096,7 +1098,7 @@
 		return -EIO;
 
 	outl(SIS_AC97_CMD_CODEC_COLD_RESET, io + SIS_AC97_CMD);
-	udelay(10);
+	udelay(250);
 
 	count = 0xffff;
 	while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count)
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 7e494b6..8c5f8b5 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -85,6 +85,7 @@
 static int ac97_clock = 48000;
 static char *ac97_quirk;
 static int dxs_support;
+static int dxs_init_volume = 31;
 static int nodelay;
 
 module_param(index, int, 0444);
@@ -103,6 +104,8 @@
 MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
 module_param(dxs_support, int, 0444);
 MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)");
+module_param(dxs_init_volume, int, 0644);
+MODULE_PARM_DESC(dxs_init_volume, "initial DXS volume (0-31)");
 module_param(nodelay, int, 0444);
 MODULE_PARM_DESC(nodelay, "Disable 500ms init delay");
 
@@ -1245,8 +1248,10 @@
 		return err;
 	stream = viadev->reg_offset / 0x10;
 	if (chip->dxs_controls[stream]) {
-		chip->playback_volume[stream][0] = 0;
-		chip->playback_volume[stream][1] = 0;
+		chip->playback_volume[stream][0] =
+				VIA_DXS_MAX_VOLUME - (dxs_init_volume & 31);
+		chip->playback_volume[stream][1] =
+				VIA_DXS_MAX_VOLUME - (dxs_init_volume & 31);
 		chip->dxs_controls[stream]->vd[0].access &=
 			~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
 		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index b1749bc..3e598e7 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -28,9 +28,13 @@
 source "sound/soc/au1x/Kconfig"
 source "sound/soc/blackfin/Kconfig"
 source "sound/soc/davinci/Kconfig"
+source "sound/soc/ep93xx/Kconfig"
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/imx/Kconfig"
+source "sound/soc/jz4740/Kconfig"
+source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
+source "sound/soc/kirkwood/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/s6000/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 1470141..eb18344 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -6,9 +6,13 @@
 obj-$(CONFIG_SND_SOC)	+= au1x/
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= davinci/
+obj-$(CONFIG_SND_SOC)	+= ep93xx/
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
+obj-$(CONFIG_SND_SOC)	+= jz4740/
+obj-$(CONFIG_SND_SOC)	+= nuc900/
 obj-$(CONFIG_SND_SOC)	+= omap/
+obj-$(CONFIG_SND_SOC)	+= kirkwood/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= s3c24xx/
 obj-$(CONFIG_SND_SOC)	+= s6000/
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
index f6b3cc0..dc5249f 100644
--- a/sound/soc/atmel/atmel-pcm.c
+++ b/sound/soc/atmel/atmel-pcm.c
@@ -77,7 +77,6 @@
 	size_t period_size;
 
 	dma_addr_t period_ptr;		/* physical address of next period */
-	int periods;			/* period index of period_ptr */
 
 	/* PDC register save */
 	u32 pdc_xpr_save;
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 0b59806..c85844d 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -549,7 +549,6 @@
 		printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
 			ssc_p->daifmt);
 		return -EINVAL;
-		break;
 	}
 	pr_debug("atmel_ssc_hw_params: "
 			"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index a61ccd2..d14a5a9 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -375,12 +375,10 @@
 	}
 
 	ret = -EBUSY;
-	wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
-					"au1xpsc_ac97");
-	if (!wd->ioarea)
+	if (!request_mem_region(r->start, resource_size(r), pdev->name))
 		goto out0;
 
-	wd->mmio = ioremap(r->start, 0xffff);
+	wd->mmio = ioremap(r->start, resource_size(r));
 	if (!wd->mmio)
 		goto out1;
 
@@ -410,8 +408,7 @@
 
 	snd_soc_unregister_dai(&au1xpsc_ac97_dai);
 out1:
-	release_resource(wd->ioarea);
-	kfree(wd->ioarea);
+	release_mem_region(r->start, resource_size(r));
 out0:
 	kfree(wd);
 	return ret;
@@ -420,6 +417,7 @@
 static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
 {
 	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+	struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
 	if (wd->dmapd)
 		au1xpsc_pcm_destroy(wd->dmapd);
@@ -433,8 +431,7 @@
 	au_sync();
 
 	iounmap(wd->mmio);
-	release_resource(wd->ioarea);
-	kfree(wd->ioarea);
+	release_mem_region(r->start, resource_size(r));
 	kfree(wd);
 
 	au1xpsc_ac97_workdata = NULL;	/* MDEV */
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index 24454c9..6083fe77 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -321,12 +321,10 @@
 	}
 
 	ret = -EBUSY;
-	wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
-					"au1xpsc_i2s");
-	if (!wd->ioarea)
+	if (!request_mem_region(r->start, resource_size(r), pdev->name))
 		goto out0;
 
-	wd->mmio = ioremap(r->start, 0xffff);
+	wd->mmio = ioremap(r->start, resource_size(r));
 	if (!wd->mmio)
 		goto out1;
 
@@ -362,8 +360,7 @@
 
 	snd_soc_unregister_dai(&au1xpsc_i2s_dai);
 out1:
-	release_resource(wd->ioarea);
-	kfree(wd->ioarea);
+	release_mem_region(r->start, resource_size(r));
 out0:
 	kfree(wd);
 	return ret;
@@ -372,6 +369,7 @@
 static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
 {
 	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+	struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
 	if (wd->dmapd)
 		au1xpsc_pcm_destroy(wd->dmapd);
@@ -384,8 +382,7 @@
 	au_sync();
 
 	iounmap(wd->mmio);
-	release_resource(wd->ioarea);
-	kfree(wd->ioarea);
+	release_mem_region(r->start, resource_size(r));
 	kfree(wd);
 
 	au1xpsc_i2s_workdata = NULL;	/* MDEV */
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index 32d3807..093775d 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -32,7 +32,6 @@
 	unsigned long rate;
 
 	unsigned long pm[2];
-	struct resource *ioarea;
 	struct mutex lock;
 	struct platform_device *dmapd;
 };
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index 523b7fc..c0eba51 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -255,8 +255,7 @@
 #ifdef CONFIG_PM
 static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
 {
-	struct sport_device *sport =
-		(struct sport_device *)dai->private_data;
+	struct sport_device *sport = dai->private_data;
 
 	pr_debug("%s : sport %d\n", __func__, dai->id);
 	if (!dai->active)
@@ -271,8 +270,7 @@
 static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
 {
 	int ret;
-	struct sport_device *sport =
-		(struct sport_device *)dai->private_data;
+	struct sport_device *sport = dai->private_data;
 
 	pr_debug("%s : sport %d\n", __func__, dai->id);
 	if (!dai->active)
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
index 4b36012..24c1426 100644
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -210,8 +210,7 @@
 #ifdef CONFIG_PM
 static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
 {
-	struct sport_device *sport =
-		(struct sport_device *)dai->private_data;
+	struct sport_device *sport = dai->private_data;
 
 	if (!dai->active)
 		return 0;
@@ -225,8 +224,7 @@
 static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
 {
 	int ret;
-	struct sport_device *sport =
-		(struct sport_device *)dai->private_data;
+	struct sport_device *sport = dai->private_data;
 
 	if (!dai->active)
 		return 0;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 5da30eb..83f5c67 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -22,9 +22,11 @@
 	select SND_SOC_AK4642 if I2C
 	select SND_SOC_AK4671 if I2C
 	select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
+	select SND_SOC_CS42L51 if I2C
 	select SND_SOC_CS4270 if I2C
-	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_DA7210 if I2C
+	select SND_SOC_JZ4740 if SOC_JZ4740
+	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_PCM3008
 	select SND_SOC_SPDIF
 	select SND_SOC_SSM2602 if I2C
@@ -48,6 +50,7 @@
 	select SND_SOC_WM8727
 	select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
+	select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
@@ -120,13 +123,13 @@
 config SND_SOC_CQ0093VC
 	tristate
 
+config SND_SOC_CS42L51
+	tristate
+
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
 	tristate
 
-config SND_SOC_DA7210
-        tristate
-
 # Cirrus Logic CS4270 Codec VD = 3.3V Errata
 # Select if you are affected by the errata where the part will not function
 # if MCLK divide-by-1.5 is selected and VD is set to 3.3V.  The driver will
@@ -138,9 +141,15 @@
 config SND_SOC_CX20442
 	tristate
 
+config SND_SOC_JZ4740_CODEC
+	tristate
+
 config SND_SOC_L3
        tristate
 
+config SND_SOC_DA7210
+        tristate
+
 config SND_SOC_PCM3008
        tristate
 
@@ -206,6 +215,9 @@
 config SND_SOC_WM8731
 	tristate
 
+config SND_SOC_WM8741
+	tristate
+
 config SND_SOC_WM8750
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 91429ea..5352409 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -9,6 +9,7 @@
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
 snd-soc-cq93vc-objs := cq93vc.o
+snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
@@ -34,6 +35,7 @@
 snd-soc-wm8727-objs := wm8727.o
 snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
+snd-soc-wm8741-objs := wm8741.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
 snd-soc-wm8776-objs := wm8776.o
@@ -56,6 +58,7 @@
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
 snd-soc-wm-hubs-objs := wm_hubs.o
+snd-soc-jz4740-codec-objs := jz4740.o
 
 # Amp
 snd-soc-max9877-objs := max9877.o
@@ -74,10 +77,12 @@
 obj-$(CONFIG_SND_SOC_AK4642)	+= snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)	+= snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
+obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
+obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
@@ -99,6 +104,7 @@
 obj-$(CONFIG_SND_SOC_WM8727)	+= snd-soc-wm8727.o
 obj-$(CONFIG_SND_SOC_WM8728)	+= snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
+obj-$(CONFIG_SND_SOC_WM8741)	+= snd-soc-wm8741.o
 obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
 obj-$(CONFIG_SND_SOC_WM8776)	+= snd-soc-wm8776.o
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 2175384..a01006c 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -272,6 +272,7 @@
 
 	if (ad1836_codec) {
 		dev_err(codec->dev, "Another ad1836 is registered\n");
+		kfree(ad1836);
 		return -EINVAL;
 	}
 
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index c8ca114..1def75e 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -24,6 +24,7 @@
 
 /* codec private data */
 struct ad193x_priv {
+	unsigned int sysclk;
 	struct snd_soc_codec codec;
 	u8 reg_cache[AD193X_NUM_REGS];
 };
@@ -251,15 +252,32 @@
 	return 0;
 }
 
+static int ad193x_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 ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+	switch (freq) {
+	case 12288000:
+	case 18432000:
+	case 24576000:
+	case 36864000:
+		ad193x->sysclk = freq;
+		return 0;
+	}
+	return -EINVAL;
+}
+
 static int ad193x_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params,
 		struct snd_soc_dai *dai)
 {
-	int word_len = 0, reg = 0;
+	int word_len = 0, reg = 0, master_rate = 0;
 
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
+	struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
 
 	/* bit size */
 	switch (params_format(params)) {
@@ -275,6 +293,25 @@
 		break;
 	}
 
+	switch (ad193x->sysclk) {
+	case 12288000:
+		master_rate = AD193X_PLL_INPUT_256;
+		break;
+	case 18432000:
+		master_rate = AD193X_PLL_INPUT_384;
+		break;
+	case 24576000:
+		master_rate = AD193X_PLL_INPUT_512;
+		break;
+	case 36864000:
+		master_rate = AD193X_PLL_INPUT_768;
+		break;
+	}
+
+	reg = snd_soc_read(codec, AD193X_PLL_CLK_CTRL0);
+	reg = (reg & AD193X_PLL_INPUT_MASK) | master_rate;
+	snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, reg);
+
 	reg = snd_soc_read(codec, AD193X_DAC_CTRL2);
 	reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len;
 	snd_soc_write(codec, AD193X_DAC_CTRL2, reg);
@@ -348,6 +385,7 @@
 	/* pll input: mclki/xi */
 	snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
 	snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04);
+	ad193x->sysclk = 12288000;
 
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
@@ -383,6 +421,7 @@
 	.hw_params = ad193x_hw_params,
 	.digital_mute = ad193x_mute,
 	.set_tdm_slot = ad193x_set_tdm_slot,
+	.set_sysclk	= ad193x_set_dai_sysclk,
 	.set_fmt = ad193x_set_dai_fmt,
 };
 
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index a03c880..654ba64 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -11,6 +11,11 @@
 
 #define AD193X_PLL_CLK_CTRL0    0x800
 #define AD193X_PLL_POWERDOWN           0x01
+#define AD193X_PLL_INPUT_MASK   (~0x6)
+#define AD193X_PLL_INPUT_256    (0 << 1)
+#define AD193X_PLL_INPUT_384    (1 << 1)
+#define AD193X_PLL_INPUT_512    (2 << 1)
+#define AD193X_PLL_INPUT_768    (3 << 1)
 #define AD193X_PLL_CLK_CTRL1    0x801
 #define AD193X_DAC_CTRL0        0x802
 #define AD193X_DAC_POWERDOWN           0x01
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 7528a54..3d7dc55 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -22,20 +22,13 @@
  * AK4643 is tested.
  */
 
-#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 <linux/slab.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 "ak4642.h"
 
@@ -111,6 +104,23 @@
 
 struct snd_soc_codec_device soc_codec_dev_ak4642;
 
+/*
+ * Playback Volume (table 39)
+ *
+ * max : 0x00 : +12.0 dB
+ *       ( 0.5 dB step )
+ * min : 0xFE : -115.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -11500, 50, 1);
+
+static const struct snd_kcontrol_new ak4642_snd_controls[] = {
+
+	SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC,
+			 0, 0xFF, 1, out_tlv),
+};
+
+
 /* codec private data */
 struct ak4642_priv {
 	struct snd_soc_codec codec;
@@ -204,7 +214,6 @@
 		 *
 		 * PLL, Master Mode
 		 * Audio I/F Format :MSB justified (ADC & DAC)
-		 * Digital Volume: -8dB
 		 * Bass Boost Level : Middle
 		 *
 		 * This operation came from example code of
@@ -214,8 +223,6 @@
 		ak4642_write(codec, 0x0e, 0x19);
 		ak4642_write(codec, 0x09, 0x91);
 		ak4642_write(codec, 0x0c, 0x91);
-		ak4642_write(codec, 0x0a, 0x28);
-		ak4642_write(codec, 0x0d, 0x28);
 		ak4642_write(codec, 0x00, 0x64);
 		snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK,	PMHP);
 		snd_soc_update_bits(codec, PW_MGMT2, HPMTN,	HPMTN);
@@ -491,8 +498,10 @@
 	codec->control_data = i2c;
 
 	ret = ak4642_init(ak4642);
-	if (ret < 0)
+	if (ret < 0) {
 		printk(KERN_ERR "failed to initialise AK4642\n");
+		kfree(ak4642);
+	}
 
 	return ret;
 }
@@ -548,6 +557,9 @@
 		goto pcm_err;
 	}
 
+	snd_soc_add_controls(ak4642_codec, ak4642_snd_controls,
+			     ARRAY_SIZE(ak4642_snd_controls));
+
 	dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
 	return ret;
 
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
new file mode 100644
index 0000000..dd9b855
--- /dev/null
+++ b/sound/soc/codecs/cs42l51.c
@@ -0,0 +1,763 @@
+/*
+ * cs42l51.c
+ *
+ * ASoC Driver for Cirrus Logic CS42L51 codecs
+ *
+ * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
+ *
+ * Based on cs4270.c - Copyright (c) Freescale Semiconductor
+ *
+ * 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.
+ *
+ * For now:
+ *  - Only I2C is support. Not SPI
+ *  - master mode *NOT* supported
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm.h>
+#include <linux/i2c.h>
+
+#include "cs42l51.h"
+
+enum master_slave_mode {
+	MODE_SLAVE,
+	MODE_SLAVE_AUTO,
+	MODE_MASTER,
+};
+
+struct cs42l51_private {
+	unsigned int mclk;
+	unsigned int audio_mode;	/* The mode (I2S or left-justified) */
+	enum master_slave_mode func;
+	struct snd_soc_codec codec;
+	u8 reg_cache[CS42L51_NUMREGS];
+};
+
+static struct snd_soc_codec *cs42l51_codec;
+
+#define CS42L51_FORMATS ( \
+		SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
+		SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
+		SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
+		SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
+
+static int cs42l51_fill_cache(struct snd_soc_codec *codec)
+{
+	u8 *cache = codec->reg_cache + 1;
+	struct i2c_client *i2c_client = codec->control_data;
+	s32 length;
+
+	length = i2c_smbus_read_i2c_block_data(i2c_client,
+			CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache);
+	if (length != CS42L51_NUMREGS) {
+		dev_err(&i2c_client->dev,
+				"I2C read failure, addr=0x%x (ret=%d vs %d)\n",
+				i2c_client->addr, length, CS42L51_NUMREGS);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int cs42l51_i2c_probe(struct i2c_client *i2c_client,
+	const struct i2c_device_id *id)
+{
+	struct snd_soc_codec *codec;
+	struct cs42l51_private *cs42l51;
+	int ret = 0;
+	int reg;
+
+	if (cs42l51_codec)
+		return -EBUSY;
+
+	/* Verify that we have a CS42L51 */
+	ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "failed to read I2C\n");
+		goto error;
+	}
+
+	if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
+	    (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
+		dev_err(&i2c_client->dev, "Invalid chip id\n");
+		ret = -ENODEV;
+		goto error;
+	}
+
+	dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n",
+				ret & 7);
+
+	cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL);
+	if (!cs42l51) {
+		dev_err(&i2c_client->dev, "could not allocate codec\n");
+		return -ENOMEM;
+	}
+	codec = &cs42l51->codec;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->dev = &i2c_client->dev;
+	codec->name = "CS42L51";
+	codec->owner = THIS_MODULE;
+	codec->dai = &cs42l51_dai;
+	codec->num_dai = 1;
+	snd_soc_codec_set_drvdata(codec, cs42l51);
+
+	codec->control_data = i2c_client;
+	codec->reg_cache = cs42l51->reg_cache;
+	codec->reg_cache_size = CS42L51_NUMREGS;
+	i2c_set_clientdata(i2c_client, codec);
+
+	ret = cs42l51_fill_cache(codec);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "failed to fill register cache\n");
+		goto error_alloc;
+	}
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Failed to set cache I/O: %d\n", ret);
+		goto error_alloc;
+	}
+
+	/*
+	 * DAC configuration
+	 * - Use signal processor
+	 * - auto mute
+	 * - vol changes immediate
+	 * - no de-emphasize
+	 */
+	reg = CS42L51_DAC_CTL_DATA_SEL(1)
+		| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
+	ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
+	if (ret < 0)
+		goto error_alloc;
+
+	cs42l51_dai.dev = codec->dev;
+	cs42l51_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto error_alloc;
+	}
+
+	ret = snd_soc_register_dai(&cs42l51_dai);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "failed to register DAIe\n");
+		goto error_reg;
+	}
+
+	return 0;
+
+error_reg:
+	snd_soc_unregister_codec(codec);
+error_alloc:
+	kfree(cs42l51);
+error:
+	return ret;
+}
+
+static int cs42l51_i2c_remove(struct i2c_client *client)
+{
+	struct cs42l51_private *cs42l51 = i2c_get_clientdata(client);
+	snd_soc_unregister_dai(&cs42l51_dai);
+	snd_soc_unregister_codec(cs42l51_codec);
+	cs42l51_codec = NULL;
+	kfree(cs42l51);
+	return 0;
+}
+
+
+static const struct i2c_device_id cs42l51_id[] = {
+	{"cs42l51", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cs42l51_id);
+
+static struct i2c_driver cs42l51_i2c_driver = {
+	.driver = {
+		.name = "CS42L51 I2C",
+		.owner = THIS_MODULE,
+	},
+	.id_table = cs42l51_id,
+	.probe = cs42l51_i2c_probe,
+	.remove = cs42l51_i2c_remove,
+};
+
+static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
+
+	switch (value) {
+	default:
+	case 0:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	/* same value : (L+R)/2 and (R+L)/2 */
+	case 1:
+	case 2:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case 3:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	}
+
+	return 0;
+}
+
+#define CHAN_MIX_NORMAL	0x00
+#define CHAN_MIX_BOTH	0x55
+#define CHAN_MIX_SWAP	0xFF
+
+static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned char val;
+
+	switch (ucontrol->value.integer.value[0]) {
+	default:
+	case 0:
+		val = CHAN_MIX_NORMAL;
+		break;
+	case 1:
+		val = CHAN_MIX_BOTH;
+		break;
+	case 2:
+		val = CHAN_MIX_SWAP;
+		break;
+	}
+
+	snd_soc_write(codec, CS42L51_PCM_MIXER, val);
+
+	return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
+static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
+/* This is a lie. after -102 db, it stays at -102 */
+/* maybe a range would be better */
+static const DECLARE_TLV_DB_SCALE(aout_tlv, -11550, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
+static const char *chan_mix[] = {
+	"L R",
+	"L+R",
+	"R L",
+};
+
+static const struct soc_enum cs42l51_chan_mix =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix);
+
+static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
+	SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
+			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
+			7, 0xffffff99, 0x18, adc_pcm_tlv),
+	SOC_DOUBLE_R("PCM Playback Switch",
+			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
+	SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
+			CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
+			8, 0xffffff19, 0x18, aout_tlv),
+	SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
+			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
+			7, 0xffffff99, 0x18, adc_pcm_tlv),
+	SOC_DOUBLE_R("ADC Mixer Switch",
+			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
+	SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
+	SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
+	SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
+	SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
+	SOC_DOUBLE_TLV("Mic Boost Volume",
+			CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
+	SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
+	SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
+	SOC_ENUM_EXT("PCM channel mixer",
+			cs42l51_chan_mix,
+			cs42l51_get_chan_mix, cs42l51_set_chan_mix),
+};
+
+/*
+ * to power down, one must:
+ * 1.) Enable the PDN bit
+ * 2.) enable power-down for the select channels
+ * 3.) disable the PDN bit.
+ */
+static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	unsigned long value;
+
+	value = snd_soc_read(w->codec, CS42L51_POWER_CTL1);
+	value &= ~CS42L51_POWER_CTL1_PDN;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMD:
+		value |= CS42L51_POWER_CTL1_PDN;
+		break;
+	default:
+	case SND_SOC_DAPM_POST_PMD:
+		break;
+	}
+	snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+		CS42L51_POWER_CTL1_PDN, value);
+
+	return 0;
+}
+
+static const char *cs42l51_dac_names[] = {"Direct PCM",
+	"DSP PCM", "ADC"};
+static const struct soc_enum cs42l51_dac_mux_enum =
+	SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names);
+static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
+	SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
+
+static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
+	"MIC Left", "MIC+preamp Left"};
+static const struct soc_enum cs42l51_adcl_mux_enum =
+	SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names);
+static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
+	SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
+
+static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
+	"MIC Right", "MIC+preamp Right"};
+static const struct soc_enum cs42l51_adcr_mux_enum =
+	SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names);
+static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
+	SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
+
+static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
+	SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
+	SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
+		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
+		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
+		CS42L51_POWER_CTL1, 1, 1,
+		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
+		CS42L51_POWER_CTL1, 2, 1,
+		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
+		CS42L51_POWER_CTL1, 5, 1,
+		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+	SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
+		CS42L51_POWER_CTL1, 6, 1,
+		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
+
+	/* analog/mic */
+	SND_SOC_DAPM_INPUT("AIN1L"),
+	SND_SOC_DAPM_INPUT("AIN1R"),
+	SND_SOC_DAPM_INPUT("AIN2L"),
+	SND_SOC_DAPM_INPUT("AIN2R"),
+	SND_SOC_DAPM_INPUT("MICL"),
+	SND_SOC_DAPM_INPUT("MICR"),
+
+	SND_SOC_DAPM_MIXER("Mic Preamp Left",
+		CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
+	SND_SOC_DAPM_MIXER("Mic Preamp Right",
+		CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
+
+	/* HP */
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+
+	/* mux */
+	SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
+		&cs42l51_dac_mux_controls),
+	SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
+		&cs42l51_adcl_mux_controls),
+	SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
+		&cs42l51_adcr_mux_controls),
+};
+
+static const struct snd_soc_dapm_route cs42l51_routes[] = {
+	{"HPL", NULL, "Left DAC"},
+	{"HPR", NULL, "Right DAC"},
+
+	{"Left ADC", NULL, "Left PGA"},
+	{"Right ADC", NULL, "Right PGA"},
+
+	{"Mic Preamp Left",  NULL,  "MICL"},
+	{"Mic Preamp Right", NULL,  "MICR"},
+
+	{"PGA-ADC Mux Left",  "AIN1 Left",        "AIN1L" },
+	{"PGA-ADC Mux Left",  "AIN2 Left",        "AIN2L" },
+	{"PGA-ADC Mux Left",  "MIC Left",         "MICL"  },
+	{"PGA-ADC Mux Left",  "MIC+preamp Left",  "Mic Preamp Left" },
+	{"PGA-ADC Mux Right", "AIN1 Right",       "AIN1R" },
+	{"PGA-ADC Mux Right", "AIN2 Right",       "AIN2R" },
+	{"PGA-ADC Mux Right", "MIC Right",        "MICR" },
+	{"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
+
+	{"Left PGA", NULL, "PGA-ADC Mux Left"},
+	{"Right PGA", NULL, "PGA-ADC Mux Right"},
+};
+
+static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int format)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_LEFT_J:
+	case SND_SOC_DAIFMT_RIGHT_J:
+		cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
+		break;
+	default:
+		dev_err(codec->dev, "invalid DAI format\n");
+		ret = -EINVAL;
+	}
+
+	switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		cs42l51->func = MODE_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		cs42l51->func = MODE_SLAVE_AUTO;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+struct cs42l51_ratios {
+	unsigned int ratio;
+	unsigned char speed_mode;
+	unsigned char mclk;
+};
+
+static struct cs42l51_ratios slave_ratios[] = {
+	{  512, CS42L51_QSM_MODE, 0 }, {  768, CS42L51_QSM_MODE, 0 },
+	{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
+	{ 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
+	{  256, CS42L51_HSM_MODE, 0 }, {  384, CS42L51_HSM_MODE, 0 },
+	{  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
+	{ 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
+	{  128, CS42L51_SSM_MODE, 0 }, {  192, CS42L51_SSM_MODE, 0 },
+	{  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
+	{  512, CS42L51_SSM_MODE, 0 }, {  768, CS42L51_SSM_MODE, 0 },
+	{  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
+	{  256, CS42L51_DSM_MODE, 0 }, {  384, CS42L51_DSM_MODE, 0 },
+};
+
+static struct cs42l51_ratios slave_auto_ratios[] = {
+	{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
+	{ 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
+	{  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
+	{ 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
+	{  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
+	{  512, CS42L51_SSM_MODE, 1 }, {  768, CS42L51_SSM_MODE, 1 },
+	{  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
+	{  256, CS42L51_DSM_MODE, 1 }, {  384, CS42L51_DSM_MODE, 1 },
+};
+
+static int cs42l51_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 cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+	struct cs42l51_ratios *ratios = NULL;
+	int nr_ratios = 0;
+	unsigned int rates = 0;
+	unsigned int rate_min = -1;
+	unsigned int rate_max = 0;
+	int i;
+
+	cs42l51->mclk = freq;
+
+	switch (cs42l51->func) {
+	case MODE_MASTER:
+		return -EINVAL;
+	case MODE_SLAVE:
+		ratios = slave_ratios;
+		nr_ratios = ARRAY_SIZE(slave_ratios);
+		break;
+	case MODE_SLAVE_AUTO:
+		ratios = slave_auto_ratios;
+		nr_ratios = ARRAY_SIZE(slave_auto_ratios);
+		break;
+	}
+
+	for (i = 0; i < nr_ratios; i++) {
+		unsigned int rate = freq / ratios[i].ratio;
+		rates |= snd_pcm_rate_to_rate_bit(rate);
+		if (rate < rate_min)
+			rate_min = rate;
+		if (rate > rate_max)
+			rate_max = rate;
+	}
+	rates &= ~SNDRV_PCM_RATE_KNOT;
+
+	if (!rates) {
+		dev_err(codec->dev, "could not find a valid sample rate\n");
+		return -EINVAL;
+	}
+
+	codec_dai->playback.rates = rates;
+	codec_dai->playback.rate_min = rate_min;
+	codec_dai->playback.rate_max = rate_max;
+
+	codec_dai->capture.rates = rates;
+	codec_dai->capture.rate_min = rate_min;
+	codec_dai->capture.rate_max = rate_max;
+
+	return 0;
+}
+
+static int cs42l51_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+	unsigned int i;
+	unsigned int rate;
+	unsigned int ratio;
+	struct cs42l51_ratios *ratios = NULL;
+	int nr_ratios = 0;
+	int intf_ctl, power_ctl, fmt;
+
+	switch (cs42l51->func) {
+	case MODE_MASTER:
+		return -EINVAL;
+	case MODE_SLAVE:
+		ratios = slave_ratios;
+		nr_ratios = ARRAY_SIZE(slave_ratios);
+		break;
+	case MODE_SLAVE_AUTO:
+		ratios = slave_auto_ratios;
+		nr_ratios = ARRAY_SIZE(slave_auto_ratios);
+		break;
+	}
+
+	/* Figure out which MCLK/LRCK ratio to use */
+	rate = params_rate(params);     /* Sampling rate, in Hz */
+	ratio = cs42l51->mclk / rate;    /* MCLK/LRCK ratio */
+	for (i = 0; i < nr_ratios; i++) {
+		if (ratios[i].ratio == ratio)
+			break;
+	}
+
+	if (i == nr_ratios) {
+		/* We did not find a matching ratio */
+		dev_err(codec->dev, "could not find matching ratio\n");
+		return -EINVAL;
+	}
+
+	intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
+	power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
+
+	intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
+			| CS42L51_INTF_CTL_DAC_FORMAT(7));
+	power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
+			| CS42L51_MIC_POWER_CTL_MCLK_DIV2);
+
+	switch (cs42l51->func) {
+	case MODE_MASTER:
+		intf_ctl |= CS42L51_INTF_CTL_MASTER;
+		power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+		break;
+	case MODE_SLAVE:
+		power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+		break;
+	case MODE_SLAVE_AUTO:
+		power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
+		break;
+	}
+
+	switch (cs42l51->audio_mode) {
+	case SND_SOC_DAIFMT_I2S:
+		intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
+		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+		case SNDRV_PCM_FORMAT_S16_BE:
+			fmt = CS42L51_DAC_DIF_RJ16;
+			break;
+		case SNDRV_PCM_FORMAT_S18_3LE:
+		case SNDRV_PCM_FORMAT_S18_3BE:
+			fmt = CS42L51_DAC_DIF_RJ18;
+			break;
+		case SNDRV_PCM_FORMAT_S20_3LE:
+		case SNDRV_PCM_FORMAT_S20_3BE:
+			fmt = CS42L51_DAC_DIF_RJ20;
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+		case SNDRV_PCM_FORMAT_S24_BE:
+			fmt = CS42L51_DAC_DIF_RJ24;
+			break;
+		default:
+			dev_err(codec->dev, "unknown format\n");
+			return -EINVAL;
+		}
+		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
+		break;
+	default:
+		dev_err(codec->dev, "unknown format\n");
+		return -EINVAL;
+	}
+
+	if (ratios[i].mclk)
+		power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
+
+	ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int reg;
+	int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
+
+	reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
+
+	if (mute)
+		reg |= mask;
+	else
+		reg &= ~mask;
+
+	return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
+}
+
+static struct snd_soc_dai_ops cs42l51_dai_ops = {
+	.hw_params      = cs42l51_hw_params,
+	.set_sysclk     = cs42l51_set_dai_sysclk,
+	.set_fmt        = cs42l51_set_dai_fmt,
+	.digital_mute   = cs42l51_dai_mute,
+};
+
+struct snd_soc_dai cs42l51_dai = {
+	.name = "CS42L51 HiFi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = CS42L51_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = CS42L51_FORMATS,
+	},
+	.ops = &cs42l51_dai_ops,
+};
+EXPORT_SYMBOL_GPL(cs42l51_dai);
+
+
+static int cs42l51_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (!cs42l51_codec) {
+		dev_err(&pdev->dev, "CS42L51 codec not yet registered\n");
+		return -EINVAL;
+	}
+
+	socdev->card->codec = cs42l51_codec;
+	codec = socdev->card->codec;
+
+	/* Register PCMs */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to create PCMs\n");
+		return ret;
+	}
+
+	snd_soc_add_controls(codec, cs42l51_snd_controls,
+		ARRAY_SIZE(cs42l51_snd_controls));
+	snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets,
+		ARRAY_SIZE(cs42l51_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, cs42l51_routes,
+		ARRAY_SIZE(cs42l51_routes));
+
+	return 0;
+}
+
+
+static int cs42l51_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_device_cs42l51 = {
+	.probe =	cs42l51_probe,
+	.remove =	cs42l51_remove
+};
+EXPORT_SYMBOL_GPL(soc_codec_device_cs42l51);
+
+static int __init cs42l51_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&cs42l51_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "%s: can't add i2c driver\n", __func__);
+		return ret;
+	}
+	return 0;
+}
+module_init(cs42l51_init);
+
+static void __exit cs42l51_exit(void)
+{
+	i2c_del_driver(&cs42l51_i2c_driver);
+}
+module_exit(cs42l51_exit);
+
+MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
+MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
new file mode 100644
index 0000000..8f0bd97
--- /dev/null
+++ b/sound/soc/codecs/cs42l51.h
@@ -0,0 +1,163 @@
+/*
+ * cs42l51.h
+ *
+ * ASoC Driver for Cirrus Logic CS42L51 codecs
+ *
+ * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.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.
+ */
+#ifndef _CS42L51_H
+#define _CS42L51_H
+
+#define CS42L51_CHIP_ID			0x1B
+#define CS42L51_CHIP_REV_A		0x00
+#define CS42L51_CHIP_REV_B		0x01
+
+#define CS42L51_CHIP_REV_ID		0x01
+#define CS42L51_MK_CHIP_REV(a, b)	((a)<<3|(b))
+
+#define CS42L51_POWER_CTL1		0x02
+#define CS42L51_POWER_CTL1_PDN_DACB	(1<<6)
+#define CS42L51_POWER_CTL1_PDN_DACA	(1<<5)
+#define CS42L51_POWER_CTL1_PDN_PGAB	(1<<4)
+#define CS42L51_POWER_CTL1_PDN_PGAA	(1<<3)
+#define CS42L51_POWER_CTL1_PDN_ADCB	(1<<2)
+#define CS42L51_POWER_CTL1_PDN_ADCA	(1<<1)
+#define CS42L51_POWER_CTL1_PDN		(1<<0)
+
+#define CS42L51_MIC_POWER_CTL		0x03
+#define CS42L51_MIC_POWER_CTL_AUTO	(1<<7)
+#define CS42L51_MIC_POWER_CTL_SPEED(x)	(((x)&3)<<5)
+#define CS42L51_QSM_MODE		3
+#define CS42L51_HSM_MODE		2
+#define	CS42L51_SSM_MODE		1
+#define CS42L51_DSM_MODE		0
+#define CS42L51_MIC_POWER_CTL_3ST_SP	(1<<4)
+#define CS42L51_MIC_POWER_CTL_PDN_MICB	(1<<3)
+#define CS42L51_MIC_POWER_CTL_PDN_MICA	(1<<2)
+#define CS42L51_MIC_POWER_CTL_PDN_BIAS	(1<<1)
+#define CS42L51_MIC_POWER_CTL_MCLK_DIV2	(1<<0)
+
+#define CS42L51_INTF_CTL		0x04
+#define CS42L51_INTF_CTL_LOOPBACK	(1<<7)
+#define CS42L51_INTF_CTL_MASTER		(1<<6)
+#define CS42L51_INTF_CTL_DAC_FORMAT(x)	(((x)&7)<<3)
+#define CS42L51_DAC_DIF_LJ24		0x00
+#define CS42L51_DAC_DIF_I2S		0x01
+#define CS42L51_DAC_DIF_RJ24		0x02
+#define CS42L51_DAC_DIF_RJ20		0x03
+#define CS42L51_DAC_DIF_RJ18		0x04
+#define CS42L51_DAC_DIF_RJ16		0x05
+#define CS42L51_INTF_CTL_ADC_I2S	(1<<2)
+#define CS42L51_INTF_CTL_DIGMIX		(1<<1)
+#define CS42L51_INTF_CTL_MICMIX		(1<<0)
+
+#define CS42L51_MIC_CTL			0x05
+#define CS42L51_MIC_CTL_ADC_SNGVOL	(1<<7)
+#define CS42L51_MIC_CTL_ADCD_DBOOST	(1<<6)
+#define CS42L51_MIC_CTL_ADCA_DBOOST	(1<<5)
+#define CS42L51_MIC_CTL_MICBIAS_SEL	(1<<4)
+#define CS42L51_MIC_CTL_MICBIAS_LVL(x)	(((x)&3)<<2)
+#define CS42L51_MIC_CTL_MICB_BOOST	(1<<1)
+#define CS42L51_MIC_CTL_MICA_BOOST	(1<<0)
+
+#define CS42L51_ADC_CTL			0x06
+#define CS42L51_ADC_CTL_ADCB_HPFEN	(1<<7)
+#define CS42L51_ADC_CTL_ADCB_HPFRZ	(1<<6)
+#define CS42L51_ADC_CTL_ADCA_HPFEN	(1<<5)
+#define CS42L51_ADC_CTL_ADCA_HPFRZ	(1<<4)
+#define CS42L51_ADC_CTL_SOFTB		(1<<3)
+#define CS42L51_ADC_CTL_ZCROSSB		(1<<2)
+#define CS42L51_ADC_CTL_SOFTA		(1<<1)
+#define CS42L51_ADC_CTL_ZCROSSA		(1<<0)
+
+#define CS42L51_ADC_INPUT		0x07
+#define CS42L51_ADC_INPUT_AINB_MUX(x)	(((x)&3)<<6)
+#define CS42L51_ADC_INPUT_AINA_MUX(x)	(((x)&3)<<4)
+#define CS42L51_ADC_INPUT_INV_ADCB	(1<<3)
+#define CS42L51_ADC_INPUT_INV_ADCA	(1<<2)
+#define CS42L51_ADC_INPUT_ADCB_MUTE	(1<<1)
+#define CS42L51_ADC_INPUT_ADCA_MUTE	(1<<0)
+
+#define CS42L51_DAC_OUT_CTL		0x08
+#define CS42L51_DAC_OUT_CTL_HP_GAIN(x)	(((x)&7)<<5)
+#define CS42L51_DAC_OUT_CTL_DAC_SNGVOL	(1<<4)
+#define CS42L51_DAC_OUT_CTL_INV_PCMB	(1<<3)
+#define CS42L51_DAC_OUT_CTL_INV_PCMA	(1<<2)
+#define CS42L51_DAC_OUT_CTL_DACB_MUTE	(1<<1)
+#define CS42L51_DAC_OUT_CTL_DACA_MUTE	(1<<0)
+
+#define CS42L51_DAC_CTL			0x09
+#define CS42L51_DAC_CTL_DATA_SEL(x)	(((x)&3)<<6)
+#define CS42L51_DAC_CTL_FREEZE		(1<<5)
+#define CS42L51_DAC_CTL_DEEMPH		(1<<3)
+#define CS42L51_DAC_CTL_AMUTE		(1<<2)
+#define CS42L51_DAC_CTL_DACSZ(x)	(((x)&3)<<0)
+
+#define CS42L51_ALC_PGA_CTL		0x0A
+#define CS42L51_ALC_PGB_CTL		0x0B
+#define CS42L51_ALC_PGX_ALCX_SRDIS	(1<<7)
+#define CS42L51_ALC_PGX_ALCX_ZCDIS	(1<<6)
+#define CS42L51_ALC_PGX_PGX_VOL(x)	(((x)&0x1f)<<0)
+
+#define CS42L51_ADCA_ATT		0x0C
+#define CS42L51_ADCB_ATT		0x0D
+
+#define CS42L51_ADCA_VOL		0x0E
+#define CS42L51_ADCB_VOL		0x0F
+#define CS42L51_PCMA_VOL		0x10
+#define CS42L51_PCMB_VOL		0x11
+#define CS42L51_MIX_MUTE_ADCMIX		(1<<7)
+#define CS42L51_MIX_VOLUME(x)		(((x)&0x7f)<<0)
+
+#define CS42L51_BEEP_FREQ		0x12
+#define CS42L51_BEEP_VOL		0x13
+#define CS42L51_BEEP_CONF		0x14
+
+#define CS42L51_TONE_CTL		0x15
+#define CS42L51_TONE_CTL_TREB(x)	(((x)&0xf)<<4)
+#define CS42L51_TONE_CTL_BASS(x)	(((x)&0xf)<<0)
+
+#define CS42L51_AOUTA_VOL		0x16
+#define CS42L51_AOUTB_VOL		0x17
+#define CS42L51_PCM_MIXER		0x18
+#define CS42L51_LIMIT_THRES_DIS		0x19
+#define CS42L51_LIMIT_REL		0x1A
+#define CS42L51_LIMIT_ATT		0x1B
+#define CS42L51_ALC_EN			0x1C
+#define CS42L51_ALC_REL			0x1D
+#define CS42L51_ALC_THRES		0x1E
+#define CS42L51_NOISE_CONF		0x1F
+
+#define CS42L51_STATUS			0x20
+#define CS42L51_STATUS_SP_CLKERR	(1<<6)
+#define CS42L51_STATUS_SPEA_OVFL	(1<<5)
+#define CS42L51_STATUS_SPEB_OVFL	(1<<4)
+#define CS42L51_STATUS_PCMA_OVFL	(1<<3)
+#define CS42L51_STATUS_PCMB_OVFL	(1<<2)
+#define CS42L51_STATUS_ADCA_OVFL	(1<<1)
+#define CS42L51_STATUS_ADCB_OVFL	(1<<0)
+
+#define CS42L51_CHARGE_FREQ		0x21
+
+#define CS42L51_FIRSTREG	0x01
+/*
+ * Hack: with register 0x21, it makes 33 registers. Looks like someone in the
+ * i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using
+ * 32 regs
+ */
+#define CS42L51_LASTREG		0x20
+#define CS42L51_NUMREGS		(CS42L51_LASTREG - CS42L51_FIRSTREG + 1)
+
+extern struct snd_soc_dai cs42l51_dai;
+extern struct snd_soc_codec_device soc_codec_device_cs42l51;
+#endif
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index 75af2d6..3c51d6a 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -15,23 +15,15 @@
  * 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 <linux/slab.h>
-#include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include <sound/soc.h>
 #include <sound/soc-dapm.h>
-#include <sound/tlv.h>
 #include <sound/initval.h>
-#include <asm/div64.h>
+#include <sound/tlv.h>
 
 #include "da7210.h"
 
@@ -145,6 +137,29 @@
 
 #define DA7210_VERSION "0.0.1"
 
+/*
+ * Playback Volume
+ *
+ * max		: 0x3F (+15.0 dB)
+ *		   (1.5 dB step)
+ * min		: 0x11 (-54.0 dB)
+ * mute		: 0x10
+ * reserved	: 0x00 - 0x0F
+ *
+ * ** FIXME **
+ *
+ * Reserved area are considered as "mute".
+ * -> min = -79.5 dB
+ */
+static const DECLARE_TLV_DB_SCALE(hp_out_tlv, -7950, 150, 1);
+
+static const struct snd_kcontrol_new da7210_snd_controls[] = {
+
+	SOC_DOUBLE_R_TLV("HeadPhone Playback Volume",
+			 DA7210_HP_L_VOL, DA7210_HP_R_VOL,
+			 0, 0x3F, 0, hp_out_tlv),
+};
+
 /* Codec private data */
 struct da7210_priv {
 	struct snd_soc_codec codec;
@@ -227,10 +242,6 @@
 	struct snd_soc_codec *codec = dai->codec;
 
 	if (is_play) {
-		/* PlayBack Volume 40 */
-		snd_soc_update_bits(codec, DA7210_HP_L_VOL, 0x3F, 40);
-		snd_soc_update_bits(codec, DA7210_HP_R_VOL, 0x3F, 40);
-
 		/* Enable Out */
 		snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10);
 		snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10);
@@ -488,7 +499,7 @@
 	ret = snd_soc_register_dai(&da7210_dai);
 	if (ret) {
 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
-		goto init_err;
+		goto codec_err;
 	}
 
 	/* FIXME
@@ -574,6 +585,8 @@
 
 	return ret;
 
+codec_err:
+	snd_soc_unregister_codec(codec);
 init_err:
 	kfree(codec->reg_cache);
 	codec->reg_cache = NULL;
@@ -601,8 +614,10 @@
 	codec->control_data = i2c;
 
 	ret = da7210_init(da7210);
-	if (ret < 0)
+	if (ret < 0) {
 		pr_err("Failed to initialise da7210 audio codec\n");
+		kfree(da7210);
+	}
 
 	return ret;
 }
@@ -656,6 +671,9 @@
 	if (ret < 0)
 		goto pcm_err;
 
+	snd_soc_add_controls(da7210_codec, da7210_snd_controls,
+			     ARRAY_SIZE(da7210_snd_controls));
+
 	dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
 
 pcm_err:
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
new file mode 100644
index 0000000..66557de
--- /dev/null
+++ b/sound/soc/codecs/jz4740.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ *  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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+
+#define JZ4740_REG_CODEC_1 0x0
+#define JZ4740_REG_CODEC_2 0x1
+
+#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
+#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
+#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
+#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
+#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
+#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
+#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
+#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
+#define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18)
+#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
+#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
+#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
+#define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12)
+#define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10))
+#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9)
+#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8)
+#define JZ4740_CODEC_1_SUSPEND BIT(1)
+#define JZ4740_CODEC_1_RESET BIT(0)
+
+#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
+#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
+#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
+#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
+#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
+#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
+#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
+#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_MASK		0x1f0000
+#define JZ4740_CODEC_2_SAMPLE_RATE_MASK			0x000f00
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK		0x000030
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK	0x000003
+
+#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET		16
+#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET		 8
+#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET	 4
+#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET	 0
+
+static const uint32_t jz4740_codec_regs[] = {
+	0x021b2302, 0x00170803,
+};
+
+struct jz4740_codec {
+	void __iomem *base;
+	struct resource *mem;
+
+	uint32_t reg_cache[2];
+	struct snd_soc_codec codec;
+};
+
+static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
+{
+	return container_of(codec, struct jz4740_codec, codec);
+}
+
+static unsigned int jz4740_codec_read(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
+	return readl(jz4740_codec->base + (reg << 2));
+}
+
+static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int val)
+{
+	struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
+
+	jz4740_codec->reg_cache[reg] = val;
+	writel(val, jz4740_codec->base + (reg << 2));
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new jz4740_codec_controls[] = {
+	SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
+			JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
+	SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2,
+			JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
+	SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
+	SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
+			JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
+	SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
+	SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
+	SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
+	SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
+	SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
+	SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
+
+	SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1,
+			jz4740_codec_output_controls,
+			ARRAY_SIZE(jz4740_codec_output_controls)),
+
+	SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
+			jz4740_codec_input_controls,
+			ARRAY_SIZE(jz4740_codec_input_controls)),
+	SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+	SND_SOC_DAPM_OUTPUT("ROUT"),
+
+	SND_SOC_DAPM_INPUT("MIC"),
+	SND_SOC_DAPM_INPUT("LIN"),
+	SND_SOC_DAPM_INPUT("RIN"),
+};
+
+static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
+	{"Line Input", NULL, "LIN"},
+	{"Line Input", NULL, "RIN"},
+
+	{"Input Mixer", "Line Capture Switch", "Line Input"},
+	{"Input Mixer", "Mic Capture Switch", "MIC"},
+
+	{"ADC", NULL, "Input Mixer"},
+
+	{"Output Mixer", "Bypass Switch", "Input Mixer"},
+	{"Output Mixer", "DAC Switch", "DAC"},
+
+	{"LOUT", NULL, "Output Mixer"},
+	{"ROUT", NULL, "Output Mixer"},
+};
+
+static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	uint32_t val;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	switch (params_rate(params)) {
+	case 8000:
+		val = 0;
+		break;
+	case 11025:
+		val = 1;
+		break;
+	case 12000:
+		val = 2;
+		break;
+	case 16000:
+		val = 3;
+		break;
+	case 22050:
+		val = 4;
+		break;
+	case 24000:
+		val = 5;
+		break;
+	case 32000:
+		val = 6;
+		break;
+	case 44100:
+		val = 7;
+		break;
+	case 48000:
+		val = 8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
+
+	snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
+				JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
+	.hw_params = jz4740_codec_hw_params,
+};
+
+struct snd_soc_dai jz4740_codec_dai = {
+	.name = "jz4740",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+	},
+	.ops = &jz4740_codec_dai_ops,
+	.symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(jz4740_codec_dai);
+
+static void jz4740_codec_wakeup(struct snd_soc_codec *codec)
+{
+	int i;
+	uint32_t *cache = codec->reg_cache;
+
+	snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+		JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
+	udelay(2);
+
+	snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+		JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
+
+	for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i)
+		jz4740_codec_write(codec, i, cache[i]);
+}
+
+static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	unsigned int mask;
+	unsigned int value;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		mask = JZ4740_CODEC_1_VREF_DISABLE |
+				JZ4740_CODEC_1_VREF_AMP_DISABLE |
+				JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
+		value = 0;
+
+		snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* The only way to clear the suspend flag is to reset the codec */
+		if (codec->bias_level == SND_SOC_BIAS_OFF)
+			jz4740_codec_wakeup(codec);
+
+		mask = JZ4740_CODEC_1_VREF_DISABLE |
+			JZ4740_CODEC_1_VREF_AMP_DISABLE |
+			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
+		value = JZ4740_CODEC_1_VREF_DISABLE |
+			JZ4740_CODEC_1_VREF_AMP_DISABLE |
+			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
+
+		snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+		break;
+	case SND_SOC_BIAS_OFF:
+		mask = JZ4740_CODEC_1_SUSPEND;
+		value = JZ4740_CODEC_1_SUSPEND;
+
+		snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+		break;
+	default:
+		break;
+	}
+
+	codec->bias_level = level;
+
+	return 0;
+}
+
+static struct snd_soc_codec *jz4740_codec_codec;
+
+static int jz4740_codec_dev_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = jz4740_codec_codec;
+
+	BUG_ON(!codec);
+
+	socdev->card->codec = codec;
+
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
+		return ret;
+	}
+
+	snd_soc_add_controls(codec, jz4740_codec_controls,
+		ARRAY_SIZE(jz4740_codec_controls));
+
+	snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
+		ARRAY_SIZE(jz4740_codec_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
+		ARRAY_SIZE(jz4740_codec_dapm_routes));
+
+	snd_soc_dapm_new_widgets(codec);
+
+	return 0;
+}
+
+static int jz4740_codec_dev_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int jz4740_codec_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int jz4740_codec_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+}
+
+#else
+#define jz4740_codec_suspend NULL
+#define jz4740_codec_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
+	.probe = jz4740_codec_dev_probe,
+	.remove = jz4740_codec_dev_remove,
+	.suspend = jz4740_codec_suspend,
+	.resume = jz4740_codec_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
+
+static int __devinit jz4740_codec_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct jz4740_codec *jz4740_codec;
+	struct snd_soc_codec *codec;
+	struct resource *mem;
+
+	jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
+	if (!jz4740_codec)
+		return -ENOMEM;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
+		ret = -ENOENT;
+		goto err_free_codec;
+	}
+
+	mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
+	if (!mem) {
+		dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+		ret = -EBUSY;
+		goto err_free_codec;
+	}
+
+	jz4740_codec->base = ioremap(mem->start, resource_size(mem));
+	if (!jz4740_codec->base) {
+		dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+		ret = -EBUSY;
+		goto err_release_mem_region;
+	}
+	jz4740_codec->mem = mem;
+
+	jz4740_codec_dai.dev = &pdev->dev;
+
+	codec = &jz4740_codec->codec;
+
+	codec->dev		= &pdev->dev;
+	codec->name		= "jz4740";
+	codec->owner		= THIS_MODULE;
+
+	codec->read		= jz4740_codec_read;
+	codec->write		= jz4740_codec_write;
+	codec->set_bias_level	= jz4740_codec_set_bias_level;
+	codec->bias_level	= SND_SOC_BIAS_OFF;
+
+	codec->dai		= &jz4740_codec_dai;
+	codec->num_dai		= 1;
+
+	codec->reg_cache	= jz4740_codec->reg_cache;
+	codec->reg_cache_size	= 2;
+	memcpy(codec->reg_cache, jz4740_codec_regs, sizeof(jz4740_codec_regs));
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	jz4740_codec_codec = codec;
+
+	snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+			JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
+
+	platform_set_drvdata(pdev, jz4740_codec);
+
+	ret = snd_soc_register_codec(codec);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register codec\n");
+		goto err_iounmap;
+	}
+
+	ret = snd_soc_register_dai(&jz4740_codec_dai);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register codec dai\n");
+		goto err_unregister_codec;
+	}
+
+	jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+
+err_unregister_codec:
+	snd_soc_unregister_codec(codec);
+err_iounmap:
+	iounmap(jz4740_codec->base);
+err_release_mem_region:
+	release_mem_region(mem->start, resource_size(mem));
+err_free_codec:
+	kfree(jz4740_codec);
+
+	return ret;
+}
+
+static int __devexit jz4740_codec_remove(struct platform_device *pdev)
+{
+	struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
+	struct resource *mem = jz4740_codec->mem;
+
+	snd_soc_unregister_dai(&jz4740_codec_dai);
+	snd_soc_unregister_codec(&jz4740_codec->codec);
+
+	iounmap(jz4740_codec->base);
+	release_mem_region(mem->start, resource_size(mem));
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(jz4740_codec);
+
+	return 0;
+}
+
+static struct platform_driver jz4740_codec_driver = {
+	.probe = jz4740_codec_probe,
+	.remove = __devexit_p(jz4740_codec_remove),
+	.driver = {
+		.name = "jz4740-codec",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init jz4740_codec_init(void)
+{
+	return platform_driver_register(&jz4740_codec_driver);
+}
+module_init(jz4740_codec_init);
+
+static void __exit jz4740_codec_exit(void)
+{
+	platform_driver_unregister(&jz4740_codec_driver);
+}
+module_exit(jz4740_codec_exit);
+
+MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:jz4740-codec");
diff --git a/sound/soc/codecs/jz4740.h b/sound/soc/codecs/jz4740.h
new file mode 100644
index 0000000..b5a0691
--- /dev/null
+++ b/sound/soc/codecs/jz4740.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
+#define __SND_SOC_CODECS_JZ4740_CODEC_H__
+
+extern struct snd_soc_dai jz4740_codec_dai;
+extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
+
+#endif
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
index a631911..9119836 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transciever.c
@@ -16,8 +16,10 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/slab.h>
 #include <sound/soc.h>
 #include <sound/pcm.h>
+#include <sound/initval.h>
 
 #include "spdif_transciever.h"
 
@@ -26,6 +28,48 @@
 #define STUB_RATES	SNDRV_PCM_RATE_8000_96000
 #define STUB_FORMATS	SNDRV_PCM_FMTBIT_S16_LE
 
+static struct snd_soc_codec *spdif_dit_codec;
+
+static int spdif_dit_codec_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret;
+
+	if (spdif_dit_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = spdif_dit_codec;
+	codec = spdif_dit_codec;
+
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto err_create_pcms;
+	}
+
+	return 0;
+
+err_create_pcms:
+	return ret;
+}
+
+static int spdif_dit_codec_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_spdif_dit = {
+	.probe		= spdif_dit_codec_probe,
+	.remove		= spdif_dit_codec_remove,
+}; EXPORT_SYMBOL_GPL(soc_codec_dev_spdif_dit);
+
 struct snd_soc_dai dit_stub_dai = {
 	.name		= "DIT",
 	.playback 	= {
@@ -40,13 +84,61 @@
 
 static int spdif_dit_probe(struct platform_device *pdev)
 {
+	struct snd_soc_codec *codec;
+	int ret;
+
+	if (spdif_dit_codec) {
+		dev_err(&pdev->dev, "Another Codec is registered\n");
+		ret = -EINVAL;
+		goto err_reg_codec;
+	}
+
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	codec->dev = &pdev->dev;
+
+	mutex_init(&codec->mutex);
+
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->name = "spdif-dit";
+	codec->owner = THIS_MODULE;
+	codec->dai = &dit_stub_dai;
+	codec->num_dai = 1;
+
+	spdif_dit_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err_reg_codec;
+	}
+
 	dit_stub_dai.dev = &pdev->dev;
-	return snd_soc_register_dai(&dit_stub_dai);
+	ret = snd_soc_register_dai(&dit_stub_dai);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to register dai: %d\n", ret);
+		goto err_reg_dai;
+	}
+
+	return 0;
+
+err_reg_dai:
+	snd_soc_unregister_codec(codec);
+err_reg_codec:
+	kfree(spdif_dit_codec);
+	return ret;
 }
 
 static int spdif_dit_remove(struct platform_device *pdev)
 {
 	snd_soc_unregister_dai(&dit_stub_dai);
+	snd_soc_unregister_codec(spdif_dit_codec);
+	kfree(spdif_dit_codec);
+	spdif_dit_codec = NULL;
 	return 0;
 }
 
diff --git a/sound/soc/codecs/spdif_transciever.h b/sound/soc/codecs/spdif_transciever.h
index 296f2eb..1e10212 100644
--- a/sound/soc/codecs/spdif_transciever.h
+++ b/sound/soc/codecs/spdif_transciever.h
@@ -12,6 +12,7 @@
 #ifndef CODEC_STUBS_H
 #define CODEC_STUBS_H
 
+extern struct snd_soc_codec_device soc_codec_dev_spdif_dit;
 extern struct snd_soc_dai dit_stub_dai;
 
 #endif /* CODEC_STUBS_H */
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index b0bae35..0a4b0fe 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -560,13 +560,16 @@
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		/* vref/mid, osc on, dac unmute */
+		reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \
+			TLV320AIC23_DAC_OFF);
 		tlv320aic23_write(codec, TLV320AIC23_PWR, reg);
 		break;
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* everything off except vref/vmid, */
-		tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040);
+		tlv320aic23_write(codec, TLV320AIC23_PWR, reg | \
+			TLV320AIC23_CLK_OFF);
 		break;
 	case SND_SOC_BIAS_OFF:
 		/* everything off, dac mute, inactive */
@@ -615,7 +618,6 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
 
-	tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
 	tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
@@ -632,7 +634,6 @@
 		u16 val = tlv320aic23_read_reg_cache(codec, reg);
 		tlv320aic23_write(codec, reg, val);
 	}
-
 	tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	return 0;
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 65adc77..8651b01 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -49,8 +49,6 @@
 
 #define NSAMPLE_MAX		5700
 
-#define LATENCY_TIME_MS		20
-
 #define MODE7_LTHR		10
 #define MODE7_UTHR		(DAC33_BUFFER_SIZE_SAMPLES - 10)
 
@@ -62,6 +60,9 @@
 #define US_TO_SAMPLES(rate, us) \
 	(rate / (1000000 / us))
 
+#define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \
+	((samples * 5000) / ((burstrate * 5000) / (burstrate - playrate)))
+
 static void dac33_calculate_times(struct snd_pcm_substream *substream);
 static int dac33_prepare_chip(struct snd_pcm_substream *substream);
 
@@ -107,6 +108,10 @@
 					 * this */
 	enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */
 	unsigned int nsample;		/* burst read amount from host */
+	int mode1_latency;		/* latency caused by the i2c writes in
+					 * us */
+	int auto_fifo_config; 		/* Configure the FIFO based on the
+					 * period size */
 	u8 burst_bclkdiv;		/* BCLK divider value in burst mode */
 	unsigned int burst_rate;	/* Interface speed in Burst modes */
 
@@ -120,6 +125,8 @@
 					 * samples */
 	unsigned int mode7_us_to_lthr;	/* Time to reach lthr from uthr */
 
+	unsigned int uthr;
+
 	enum dac33_state state;
 };
 
@@ -442,6 +449,39 @@
 	return ret;
 }
 
+static int dac33_get_uthr(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = dac33->uthr;
+
+	return 0;
+}
+
+static int dac33_set_uthr(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	if (dac33->substream)
+		return -EBUSY;
+
+	if (dac33->uthr == ucontrol->value.integer.value[0])
+		return 0;
+
+	if (ucontrol->value.integer.value[0] < (MODE7_LTHR + 10) ||
+	    ucontrol->value.integer.value[0] > MODE7_UTHR)
+		ret = -EINVAL;
+	else
+		dac33->uthr = ucontrol->value.integer.value[0];
+
+	return ret;
+}
+
 static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
 			 struct snd_ctl_elem_value *ucontrol)
 {
@@ -503,13 +543,18 @@
 		 DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1),
 };
 
-static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
-	SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
-		 dac33_get_nsample, dac33_set_nsample),
+static const struct snd_kcontrol_new dac33_mode_snd_controls[] = {
 	SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum,
 		 dac33_get_fifo_mode, dac33_set_fifo_mode),
 };
 
+static const struct snd_kcontrol_new dac33_fifo_snd_controls[] = {
+	SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
+		dac33_get_nsample, dac33_set_nsample),
+	SOC_SINGLE_EXT("UTHR", 0, 0, MODE7_UTHR, 0,
+		 dac33_get_uthr, dac33_set_uthr),
+};
+
 /* Analog bypass */
 static const struct snd_kcontrol_new dac33_dapm_abypassl_control =
 	SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1);
@@ -612,7 +657,7 @@
 	switch (dac33->fifo_mode) {
 	case DAC33_FIFO_MODE1:
 		dac33_write16(codec, DAC33_NSAMPLE_MSB,
-			DAC33_THRREG(dac33->nsample + dac33->alarm_threshold));
+			DAC33_THRREG(dac33->nsample));
 
 		/* Take the timestamps */
 		spin_lock_irq(&dac33->lock);
@@ -761,6 +806,10 @@
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 
 	dac33->substream = NULL;
+
+	/* Reset the nSample restrictions */
+	dac33->nsample_min = 0;
+	dac33->nsample_max = NSAMPLE_MAX;
 }
 
 static int dac33_hw_params(struct snd_pcm_substream *substream,
@@ -985,7 +1034,7 @@
 		 * Configure the threshold levels, and leave 10 sample space
 		 * at the bottom, and also at the top of the FIFO
 		 */
-		dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(MODE7_UTHR));
+		dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr));
 		dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR));
 		break;
 	default:
@@ -1003,57 +1052,71 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
+	unsigned int period_size = substream->runtime->period_size;
+	unsigned int rate = substream->runtime->rate;
 	unsigned int nsample_limit;
 
 	/* In bypass mode we don't need to calculate */
 	if (!dac33->fifo_mode)
 		return;
 
-	/* Number of samples (16bit, stereo) in one period */
-	dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4;
-
-	/* Number of samples (16bit, stereo) in ALSA buffer */
-	dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4;
-	/* Subtract one period from the total */
-	dac33->nsample_max -= dac33->nsample_min;
-
-	/* Number of samples for LATENCY_TIME_MS / 2 */
-	dac33->alarm_threshold = substream->runtime->rate /
-				 (1000 / (LATENCY_TIME_MS / 2));
-
-	/* Find and fix up the lowest nsmaple limit */
-	nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS);
-
-	if (dac33->nsample_min < nsample_limit)
-		dac33->nsample_min = nsample_limit;
-
-	if (dac33->nsample < dac33->nsample_min)
-		dac33->nsample = dac33->nsample_min;
-
-	/*
-	 * Find and fix up the highest nsmaple limit
-	 * In order to not overflow the DAC33 buffer substract the
-	 * alarm_threshold value from the size of the DAC33 buffer
-	 */
-	nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold;
-
-	if (dac33->nsample_max > nsample_limit)
-		dac33->nsample_max = nsample_limit;
-
-	if (dac33->nsample > dac33->nsample_max)
-		dac33->nsample = dac33->nsample_max;
-
 	switch (dac33->fifo_mode) {
 	case DAC33_FIFO_MODE1:
+		/* Number of samples under i2c latency */
+		dac33->alarm_threshold = US_TO_SAMPLES(rate,
+						dac33->mode1_latency);
+		if (dac33->auto_fifo_config) {
+			if (period_size <= dac33->alarm_threshold)
+				/*
+				 * Configure nSamaple to number of periods,
+				 * which covers the latency requironment.
+				 */
+				dac33->nsample = period_size *
+				       ((dac33->alarm_threshold / period_size) +
+				       (dac33->alarm_threshold % period_size ?
+				       1 : 0));
+			else
+				dac33->nsample = period_size;
+		} else {
+			/* nSample time shall not be shorter than i2c latency */
+			dac33->nsample_min = dac33->alarm_threshold;
+			/*
+			 * nSample should not be bigger than alsa buffer minus
+			 * size of one period to avoid overruns
+			 */
+			dac33->nsample_max = substream->runtime->buffer_size -
+						period_size;
+			nsample_limit = DAC33_BUFFER_SIZE_SAMPLES -
+					dac33->alarm_threshold;
+			if (dac33->nsample_max > nsample_limit)
+				dac33->nsample_max = nsample_limit;
+
+			/* Correct the nSample if it is outside of the ranges */
+			if (dac33->nsample < dac33->nsample_min)
+				dac33->nsample = dac33->nsample_min;
+			if (dac33->nsample > dac33->nsample_max)
+				dac33->nsample = dac33->nsample_max;
+		}
+
 		dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate,
 						      dac33->nsample);
 		dac33->t_stamp1 = 0;
 		dac33->t_stamp2 = 0;
 		break;
 	case DAC33_FIFO_MODE7:
+		if (dac33->auto_fifo_config) {
+			dac33->uthr = UTHR_FROM_PERIOD_SIZE(
+					period_size,
+					rate,
+					dac33->burst_rate) + 9;
+			if (dac33->uthr > MODE7_UTHR)
+				dac33->uthr = MODE7_UTHR;
+			if (dac33->uthr < (MODE7_LTHR + 10))
+				dac33->uthr = (MODE7_LTHR + 10);
+		}
 		dac33->mode7_us_to_lthr =
-					SAMPLES_TO_US(substream->runtime->rate,
-						MODE7_UTHR - MODE7_LTHR + 1);
+				SAMPLES_TO_US(substream->runtime->rate,
+					dac33->uthr - MODE7_LTHR + 1);
 		dac33->t_stamp1 = 0;
 		break;
 	default:
@@ -1104,7 +1167,7 @@
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 	unsigned long long t0, t1, t_now;
-	unsigned int time_delta;
+	unsigned int time_delta, uthr;
 	int samples_out, samples_in, samples;
 	snd_pcm_sframes_t delay = 0;
 
@@ -1182,6 +1245,7 @@
 	case DAC33_FIFO_MODE7:
 		spin_lock(&dac33->lock);
 		t0 = dac33->t_stamp1;
+		uthr = dac33->uthr;
 		spin_unlock(&dac33->lock);
 		t_now = ktime_to_us(ktime_get());
 
@@ -1194,7 +1258,7 @@
 			 * Either the timestamps are messed or equal. Report
 			 * maximum delay
 			 */
-			delay = MODE7_UTHR;
+			delay = uthr;
 			goto out;
 		}
 
@@ -1208,8 +1272,8 @@
 					substream->runtime->rate,
 					time_delta);
 
-			if (likely(MODE7_UTHR > samples_out))
-				delay = MODE7_UTHR - samples_out;
+			if (likely(uthr > samples_out))
+				delay = uthr - samples_out;
 			else
 				delay = 0;
 		} else {
@@ -1227,8 +1291,8 @@
 					time_delta);
 			delay = MODE7_LTHR + samples_in - samples_out;
 
-			if (unlikely(delay > MODE7_UTHR))
-				delay = MODE7_UTHR;
+			if (unlikely(delay > uthr))
+				delay = uthr;
 		}
 		break;
 	default:
@@ -1347,10 +1411,15 @@
 
 	snd_soc_add_controls(codec, dac33_snd_controls,
 			     ARRAY_SIZE(dac33_snd_controls));
-	/* Only add the nSample controls, if we have valid IRQ number */
-	if (dac33->irq >= 0)
-		snd_soc_add_controls(codec, dac33_nsample_snd_controls,
-				     ARRAY_SIZE(dac33_nsample_snd_controls));
+	/* Only add the FIFO controls, if we have valid IRQ number */
+	if (dac33->irq >= 0) {
+		snd_soc_add_controls(codec, dac33_mode_snd_controls,
+				     ARRAY_SIZE(dac33_mode_snd_controls));
+		/* FIFO usage controls only, if autoio config is not selected */
+		if (!dac33->auto_fifo_config)
+			snd_soc_add_controls(codec, dac33_fifo_snd_controls,
+					ARRAY_SIZE(dac33_fifo_snd_controls));
+	}
 
 	dac33_add_widgets(codec);
 
@@ -1481,9 +1550,14 @@
 	/* Pre calculate the burst rate */
 	dac33->burst_rate = BURST_BASEFREQ_HZ / dac33->burst_bclkdiv / 32;
 	dac33->keep_bclk = pdata->keep_bclk;
+	dac33->auto_fifo_config = pdata->auto_fifo_config;
+	dac33->mode1_latency = pdata->mode1_latency;
+	if (!dac33->mode1_latency)
+		dac33->mode1_latency = 10000; /* 10ms */
 	dac33->irq = client->irq;
 	dac33->nsample = NSAMPLE_MAX;
 	dac33->nsample_max = NSAMPLE_MAX;
+	dac33->uthr = MODE7_UTHR;
 	/* Disable FIFO use by default */
 	dac33->fifo_mode = DAC33_FIFO_BYPASS;
 
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index b4fcdb0..7b618bb 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -43,37 +43,37 @@
  */
 static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
 	0x00, /* this register not used		*/
-	0x91, /* REG_CODEC_MODE		(0x1)	*/
-	0xc3, /* REG_OPTION		(0x2)	*/
+	0x00, /* REG_CODEC_MODE		(0x1)	*/
+	0x00, /* REG_OPTION		(0x2)	*/
 	0x00, /* REG_UNKNOWN		(0x3)	*/
 	0x00, /* REG_MICBIAS_CTL	(0x4)	*/
-	0x20, /* REG_ANAMICL		(0x5)	*/
+	0x00, /* REG_ANAMICL		(0x5)	*/
 	0x00, /* REG_ANAMICR		(0x6)	*/
 	0x00, /* REG_AVADC_CTL		(0x7)	*/
 	0x00, /* REG_ADCMICSEL		(0x8)	*/
 	0x00, /* REG_DIGMIXING		(0x9)	*/
-	0x0c, /* REG_ATXL1PGA		(0xA)	*/
-	0x0c, /* REG_ATXR1PGA		(0xB)	*/
-	0x00, /* REG_AVTXL2PGA		(0xC)	*/
-	0x00, /* REG_AVTXR2PGA		(0xD)	*/
+	0x0f, /* REG_ATXL1PGA		(0xA)	*/
+	0x0f, /* REG_ATXR1PGA		(0xB)	*/
+	0x0f, /* REG_AVTXL2PGA		(0xC)	*/
+	0x0f, /* REG_AVTXR2PGA		(0xD)	*/
 	0x00, /* REG_AUDIO_IF		(0xE)	*/
 	0x00, /* REG_VOICE_IF		(0xF)	*/
-	0x00, /* REG_ARXR1PGA		(0x10)	*/
-	0x00, /* REG_ARXL1PGA		(0x11)	*/
-	0x6c, /* REG_ARXR2PGA		(0x12)	*/
-	0x6c, /* REG_ARXL2PGA		(0x13)	*/
-	0x00, /* REG_VRXPGA		(0x14)	*/
+	0x3f, /* REG_ARXR1PGA		(0x10)	*/
+	0x3f, /* REG_ARXL1PGA		(0x11)	*/
+	0x3f, /* REG_ARXR2PGA		(0x12)	*/
+	0x3f, /* REG_ARXL2PGA		(0x13)	*/
+	0x25, /* REG_VRXPGA		(0x14)	*/
 	0x00, /* REG_VSTPGA		(0x15)	*/
 	0x00, /* REG_VRX2ARXPGA		(0x16)	*/
 	0x00, /* REG_AVDAC_CTL		(0x17)	*/
 	0x00, /* REG_ARX2VTXPGA		(0x18)	*/
-	0x00, /* REG_ARXL1_APGA_CTL	(0x19)	*/
-	0x00, /* REG_ARXR1_APGA_CTL	(0x1A)	*/
-	0x4a, /* REG_ARXL2_APGA_CTL	(0x1B)	*/
-	0x4a, /* REG_ARXR2_APGA_CTL	(0x1C)	*/
+	0x32, /* REG_ARXL1_APGA_CTL	(0x19)	*/
+	0x32, /* REG_ARXR1_APGA_CTL	(0x1A)	*/
+	0x32, /* REG_ARXL2_APGA_CTL	(0x1B)	*/
+	0x32, /* REG_ARXR2_APGA_CTL	(0x1C)	*/
 	0x00, /* REG_ATX2ARXPGA		(0x1D)	*/
 	0x00, /* REG_BT_IF		(0x1E)	*/
-	0x00, /* REG_BTPGA		(0x1F)	*/
+	0x55, /* REG_BTPGA		(0x1F)	*/
 	0x00, /* REG_BTSTPGA		(0x20)	*/
 	0x00, /* REG_EAR_CTL		(0x21)	*/
 	0x00, /* REG_HS_SEL		(0x22)	*/
@@ -85,32 +85,32 @@
 	0x00, /* REG_PRECKR_CTL		(0x28)	*/
 	0x00, /* REG_HFL_CTL		(0x29)	*/
 	0x00, /* REG_HFR_CTL		(0x2A)	*/
-	0x00, /* REG_ALC_CTL		(0x2B)	*/
+	0x05, /* REG_ALC_CTL		(0x2B)	*/
 	0x00, /* REG_ALC_SET1		(0x2C)	*/
 	0x00, /* REG_ALC_SET2		(0x2D)	*/
 	0x00, /* REG_BOOST_CTL		(0x2E)	*/
 	0x00, /* REG_SOFTVOL_CTL	(0x2F)	*/
-	0x00, /* REG_DTMF_FREQSEL	(0x30)	*/
+	0x13, /* REG_DTMF_FREQSEL	(0x30)	*/
 	0x00, /* REG_DTMF_TONEXT1H	(0x31)	*/
 	0x00, /* REG_DTMF_TONEXT1L	(0x32)	*/
 	0x00, /* REG_DTMF_TONEXT2H	(0x33)	*/
 	0x00, /* REG_DTMF_TONEXT2L	(0x34)	*/
-	0x00, /* REG_DTMF_TONOFF	(0x35)	*/
-	0x00, /* REG_DTMF_WANONOFF	(0x36)	*/
+	0x79, /* REG_DTMF_TONOFF	(0x35)	*/
+	0x11, /* REG_DTMF_WANONOFF	(0x36)	*/
 	0x00, /* REG_I2S_RX_SCRAMBLE_H	(0x37)	*/
 	0x00, /* REG_I2S_RX_SCRAMBLE_M	(0x38)	*/
 	0x00, /* REG_I2S_RX_SCRAMBLE_L	(0x39)	*/
 	0x06, /* REG_APLL_CTL		(0x3A)	*/
 	0x00, /* REG_DTMF_CTL		(0x3B)	*/
-	0x00, /* REG_DTMF_PGA_CTL2	(0x3C)	*/
-	0x00, /* REG_DTMF_PGA_CTL1	(0x3D)	*/
+	0x44, /* REG_DTMF_PGA_CTL2	(0x3C)	*/
+	0x69, /* REG_DTMF_PGA_CTL1	(0x3D)	*/
 	0x00, /* REG_MISC_SET_1		(0x3E)	*/
 	0x00, /* REG_PCMBTMUX		(0x3F)	*/
 	0x00, /* not used		(0x40)	*/
 	0x00, /* not used		(0x41)	*/
 	0x00, /* not used		(0x42)	*/
 	0x00, /* REG_RX_PATH_SEL	(0x43)	*/
-	0x00, /* REG_VDL_APGA_CTL	(0x44)	*/
+	0x32, /* REG_VDL_APGA_CTL	(0x44)	*/
 	0x00, /* REG_VIBRA_CTL		(0x45)	*/
 	0x00, /* REG_VIBRA_SET		(0x46)	*/
 	0x00, /* REG_VIBRA_PWM_SET	(0x47)	*/
@@ -143,6 +143,9 @@
 	u8 earpiece_enabled;
 	u8 predrivel_enabled, predriver_enabled;
 	u8 carkitl_enabled, carkitr_enabled;
+
+	/* Delay needed after enabling the digimic interface */
+	unsigned int digimic_delay;
 };
 
 /*
@@ -244,21 +247,112 @@
 	udelay(10);
 }
 
-static void twl4030_init_chip(struct snd_soc_codec *codec)
+static inline void twl4030_check_defaults(struct snd_soc_codec *codec)
 {
-	u8 *cache = codec->reg_cache;
-	int i;
+	int i, difference = 0;
+	u8 val;
 
-	/* clear CODECPDZ prior to setting register defaults */
-	twl4030_codec_enable(codec, 0);
+	dev_dbg(codec->dev, "Checking TWL audio default configuration\n");
+	for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) {
+		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i);
+		if (val != twl4030_reg[i]) {
+			difference++;
+			dev_dbg(codec->dev,
+				 "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n",
+				 i, val, twl4030_reg[i]);
+		}
+	}
+	dev_dbg(codec->dev, "Found %d non maching registers. %s\n",
+		 difference, difference ? "Not OK" : "OK");
+}
+
+static inline void twl4030_reset_registers(struct snd_soc_codec *codec)
+{
+	int i;
 
 	/* set all audio section registers to reasonable defaults */
 	for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
 		if (i != TWL4030_REG_APLL_CTL)
-			twl4030_write(codec, i,	cache[i]);
+			twl4030_write(codec, i, twl4030_reg[i]);
 
 }
 
+static void twl4030_init_chip(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct twl4030_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
+	u8 reg, byte;
+	int i = 0;
+
+	/* Check defaults, if instructed before anything else */
+	if (setup && setup->check_defaults)
+		twl4030_check_defaults(codec);
+
+	/* Reset registers, if no setup data or if instructed to do so */
+	if (!setup || (setup && setup->reset_registers))
+		twl4030_reset_registers(codec);
+
+	/* Refresh APLL_CTL register from HW */
+	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+			    TWL4030_REG_APLL_CTL);
+	twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte);
+
+	/* anti-pop when changing analog gain */
+	reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
+	twl4030_write(codec, TWL4030_REG_MISC_SET_1,
+		reg | TWL4030_SMOOTH_ANAVOL_EN);
+
+	twl4030_write(codec, TWL4030_REG_OPTION,
+		TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
+		TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);
+
+	/* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */
+	twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
+
+	/* Machine dependent setup */
+	if (!setup)
+		return;
+
+	twl4030->digimic_delay = setup->digimic_delay;
+
+	/* Configuration for headset ramp delay from setup data */
+	if (setup->sysclk != twl4030->sysclk)
+		dev_warn(codec->dev,
+				"Mismatch in APLL mclk: %u (configured: %u)\n",
+				setup->sysclk, twl4030->sysclk);
+
+	reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+	reg &= ~TWL4030_RAMP_DELAY;
+	reg |= (setup->ramp_delay_value << 2);
+	twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg);
+
+	/* initiate offset cancellation */
+	twl4030_codec_enable(codec, 1);
+
+	reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+	reg &= ~TWL4030_OFFSET_CNCL_SEL;
+	reg |= setup->offset_cncl_path;
+	twl4030_write(codec, TWL4030_REG_ANAMICL,
+		reg | TWL4030_CNCL_OFFSET_START);
+
+	/* wait for offset cancellation to complete */
+	do {
+		/* this takes a little while, so don't slam i2c */
+		udelay(2000);
+		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+				    TWL4030_REG_ANAMICL);
+	} while ((i++ < 100) &&
+		 ((byte & TWL4030_CNCL_OFFSET_START) ==
+		  TWL4030_CNCL_OFFSET_START));
+
+	/* Make sure that the reg_cache has the same value as the HW */
+	twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
+
+	twl4030_codec_enable(codec, 0);
+}
+
 static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
 {
 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
@@ -280,55 +374,6 @@
 		twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
 }
 
-static void twl4030_power_up(struct snd_soc_codec *codec)
-{
-	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
-	u8 anamicl, regmisc1, byte;
-	int i = 0;
-
-	if (twl4030->codec_powered)
-		return;
-
-	/* set CODECPDZ to turn on codec */
-	twl4030_codec_enable(codec, 1);
-
-	/* initiate offset cancellation */
-	anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
-	twl4030_write(codec, TWL4030_REG_ANAMICL,
-		anamicl | TWL4030_CNCL_OFFSET_START);
-
-	/* wait for offset cancellation to complete */
-	do {
-		/* this takes a little while, so don't slam i2c */
-		udelay(2000);
-		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
-				    TWL4030_REG_ANAMICL);
-	} while ((i++ < 100) &&
-		 ((byte & TWL4030_CNCL_OFFSET_START) ==
-		  TWL4030_CNCL_OFFSET_START));
-
-	/* Make sure that the reg_cache has the same value as the HW */
-	twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
-
-	/* anti-pop when changing analog gain */
-	regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
-	twl4030_write(codec, TWL4030_REG_MISC_SET_1,
-		regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
-
-	/* toggle CODECPDZ as per TRM */
-	twl4030_codec_enable(codec, 0);
-	twl4030_codec_enable(codec, 1);
-}
-
-/*
- * Unconditional power down
- */
-static void twl4030_power_down(struct snd_soc_codec *codec)
-{
-	/* power down */
-	twl4030_codec_enable(codec, 0);
-}
-
 /* Earpiece */
 static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = {
 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0),
@@ -500,10 +545,11 @@
 static const struct snd_kcontrol_new twl4030_dapm_abypassv_control =
 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0);
 
-/* Digital bypass gain, 0 mutes the bypass */
+/* Digital bypass gain, mute instead of -30dB */
 static const unsigned int twl4030_dapm_dbypass_tlv[] = {
-	TLV_DB_RANGE_HEAD(2),
-	0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1),
+	TLV_DB_RANGE_HEAD(3),
+	0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1),
+	2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0),
 	4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0),
 };
 
@@ -531,36 +577,6 @@
 			TWL4030_REG_VSTPGA, 0, 0x29, 0,
 			twl4030_dapm_dbypassv_tlv);
 
-static int micpath_event(struct snd_soc_dapm_widget *w,
-	struct snd_kcontrol *kcontrol, int event)
-{
-	struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
-	unsigned char adcmicsel, micbias_ctl;
-
-	adcmicsel = twl4030_read_reg_cache(w->codec, TWL4030_REG_ADCMICSEL);
-	micbias_ctl = twl4030_read_reg_cache(w->codec, TWL4030_REG_MICBIAS_CTL);
-	/* Prepare the bits for the given TX path:
-	 * shift_l == 0: TX1 microphone path
-	 * shift_l == 2: TX2 microphone path */
-	if (e->shift_l) {
-		/* TX2 microphone path */
-		if (adcmicsel & TWL4030_TX2IN_SEL)
-			micbias_ctl |= TWL4030_MICBIAS2_CTL; /* digimic */
-		else
-			micbias_ctl &= ~TWL4030_MICBIAS2_CTL;
-	} else {
-		/* TX1 microphone path */
-		if (adcmicsel & TWL4030_TX1IN_SEL)
-			micbias_ctl |= TWL4030_MICBIAS1_CTL; /* digimic */
-		else
-			micbias_ctl &= ~TWL4030_MICBIAS1_CTL;
-	}
-
-	twl4030_write(w->codec, TWL4030_REG_MICBIAS_CTL, micbias_ctl);
-
-	return 0;
-}
-
 /*
  * Output PGA builder:
  * Handle the muting and unmuting of the given output (turning off the
@@ -814,6 +830,16 @@
 	return 0;
 }
 
+static int digimic_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
+
+	if (twl4030->digimic_delay)
+		mdelay(twl4030->digimic_delay);
+	return 0;
+}
+
 /*
  * Some of the gain controls in TWL (mostly those which are associated with
  * the outputs) are implemented in an interesting way:
@@ -1374,14 +1400,10 @@
 	/* Analog/Digital mic path selection.
 	   TX1 Left/Right: either analog Left/Right or Digimic0
 	   TX2 Left/Right: either analog Left/Right or Digimic1 */
-	SND_SOC_DAPM_MUX_E("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_micpathtx1_control, micpath_event,
-		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
-		SND_SOC_DAPM_POST_REG),
-	SND_SOC_DAPM_MUX_E("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_micpathtx2_control, micpath_event,
-		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
-		SND_SOC_DAPM_POST_REG),
+	SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_micpathtx1_control),
+	SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_micpathtx2_control),
 
 	/* Analog input mixers for the capture amplifiers */
 	SND_SOC_DAPM_MIXER("Analog Left",
@@ -1398,10 +1420,17 @@
 	SND_SOC_DAPM_PGA("ADC Physical Right",
 		TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0),
 
-	SND_SOC_DAPM_PGA("Digimic0 Enable",
-		TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("Digimic1 Enable",
-		TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA_E("Digimic0 Enable",
+		TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0,
+		digimic_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_PGA_E("Digimic1 Enable",
+		TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0,
+		digimic_event, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0,
+			    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0,
+			    NULL, 0),
 
 	SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
 	SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
@@ -1419,8 +1448,11 @@
 	/* Supply for the digital part (APLL) */
 	{"Digital Voice Playback Mixer", NULL, "APLL Enable"},
 
-	{"Digital R1 Playback Mixer", NULL, "AIF Enable"},
-	{"Digital L1 Playback Mixer", NULL, "AIF Enable"},
+	{"DAC Left1", NULL, "AIF Enable"},
+	{"DAC Right1", NULL, "AIF Enable"},
+	{"DAC Left2", NULL, "AIF Enable"},
+	{"DAC Right1", NULL, "AIF Enable"},
+
 	{"Digital R2 Playback Mixer", NULL, "AIF Enable"},
 	{"Digital L2 Playback Mixer", NULL, "AIF Enable"},
 
@@ -1491,10 +1523,10 @@
 
 	/* outputs */
 	/* Must be always connected (for AIF and APLL) */
-	{"Virtual HiFi OUT", NULL, "Digital L1 Playback Mixer"},
-	{"Virtual HiFi OUT", NULL, "Digital R1 Playback Mixer"},
-	{"Virtual HiFi OUT", NULL, "Digital L2 Playback Mixer"},
-	{"Virtual HiFi OUT", NULL, "Digital R2 Playback Mixer"},
+	{"Virtual HiFi OUT", NULL, "DAC Left1"},
+	{"Virtual HiFi OUT", NULL, "DAC Right1"},
+	{"Virtual HiFi OUT", NULL, "DAC Left2"},
+	{"Virtual HiFi OUT", NULL, "DAC Right2"},
 	/* Must be always connected (for APLL) */
 	{"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"},
 	/* Physical outputs */
@@ -1531,6 +1563,9 @@
 	{"Digimic0 Enable", NULL, "DIGIMIC0"},
 	{"Digimic1 Enable", NULL, "DIGIMIC1"},
 
+	{"DIGIMIC0", NULL, "micbias1 select"},
+	{"DIGIMIC1", NULL, "micbias2 select"},
+
 	/* TX1 Left capture path */
 	{"TX1 Capture Route", "Analog", "ADC Physical Left"},
 	{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
@@ -1605,10 +1640,10 @@
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF)
-			twl4030_power_up(codec);
+			twl4030_codec_enable(codec, 1);
 		break;
 	case SND_SOC_BIAS_OFF:
-		twl4030_power_down(codec);
+		twl4030_codec_enable(codec, 0);
 		break;
 	}
 	codec->bias_level = level;
@@ -1794,13 +1829,6 @@
 		return -EINVAL;
 	}
 
-	if (mode != old_mode) {
-		/* change rate and set CODECPDZ */
-		twl4030_codec_enable(codec, 0);
-		twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
-		twl4030_codec_enable(codec, 1);
-	}
-
 	/* sample size */
 	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
 	format = old_format;
@@ -1818,16 +1846,20 @@
 		return -EINVAL;
 	}
 
-	if (format != old_format) {
-
-		/* clear CODECPDZ before changing format (codec requirement) */
-		twl4030_codec_enable(codec, 0);
-
-		/* change format */
-		twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
-
-		/* set CODECPDZ afterwards */
-		twl4030_codec_enable(codec, 1);
+	if (format != old_format || mode != old_mode) {
+		if (twl4030->codec_powered) {
+			/*
+			 * If the codec is powered, than we need to toggle the
+			 * codec power.
+			 */
+			twl4030_codec_enable(codec, 0);
+			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+			twl4030_codec_enable(codec, 1);
+		} else {
+			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+		}
 	}
 
 	/* Store the important parameters for the DAI configuration and set
@@ -1877,6 +1909,7 @@
 			     unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	u8 old_format, format;
 
 	/* get format */
@@ -1911,15 +1944,17 @@
 	}
 
 	if (format != old_format) {
-
-		/* clear CODECPDZ before changing format (codec requirement) */
-		twl4030_codec_enable(codec, 0);
-
-		/* change format */
-		twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
-
-		/* set CODECPDZ afterwards */
-		twl4030_codec_enable(codec, 1);
+		if (twl4030->codec_powered) {
+			/*
+			 * If the codec is powered, than we need to toggle the
+			 * codec power.
+			 */
+			twl4030_codec_enable(codec, 0);
+			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+			twl4030_codec_enable(codec, 1);
+		} else {
+			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+		}
 	}
 
 	return 0;
@@ -2011,6 +2046,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
+	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	u8 old_mode, mode;
 
 	/* Enable voice digital filters */
@@ -2035,10 +2071,17 @@
 	}
 
 	if (mode != old_mode) {
-		/* change rate and set CODECPDZ */
-		twl4030_codec_enable(codec, 0);
-		twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
-		twl4030_codec_enable(codec, 1);
+		if (twl4030->codec_powered) {
+			/*
+			 * If the codec is powered, than we need to toggle the
+			 * codec power.
+			 */
+			twl4030_codec_enable(codec, 0);
+			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+			twl4030_codec_enable(codec, 1);
+		} else {
+			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+		}
 	}
 
 	return 0;
@@ -2068,6 +2111,7 @@
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
 	u8 old_format, format;
 
 	/* get format */
@@ -2099,10 +2143,17 @@
 	}
 
 	if (format != old_format) {
-		/* change format and set CODECPDZ */
-		twl4030_codec_enable(codec, 0);
-		twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
-		twl4030_codec_enable(codec, 1);
+		if (twl4030->codec_powered) {
+			/*
+			 * If the codec is powered, than we need to toggle the
+			 * codec power.
+			 */
+			twl4030_codec_enable(codec, 0);
+			twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+			twl4030_codec_enable(codec, 1);
+		} else {
+			twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+		}
 	}
 
 	return 0;
@@ -2202,31 +2253,15 @@
 static int twl4030_soc_probe(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct twl4030_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec;
-	struct twl4030_priv *twl4030;
 	int ret;
 
 	BUG_ON(!twl4030_codec);
 
 	codec = twl4030_codec;
-	twl4030 = snd_soc_codec_get_drvdata(codec);
 	socdev->card->codec = codec;
 
-	/* Configuration for headset ramp delay from setup data */
-	if (setup) {
-		unsigned char hs_pop;
-
-		if (setup->sysclk != twl4030->sysclk)
-			dev_warn(&pdev->dev,
-				 "Mismatch in APLL mclk: %u (configured: %u)\n",
-				 setup->sysclk, twl4030->sysclk);
-
-		hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
-		hs_pop &= ~TWL4030_RAMP_DELAY;
-		hs_pop |= (setup->ramp_delay_value << 2);
-		twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
-	}
+	twl4030_init_chip(pdev);
 
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -2247,6 +2282,8 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
 
+	/* Reset registers to their chip default before leaving */
+	twl4030_reset_registers(codec);
 	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
@@ -2287,6 +2324,7 @@
 	codec->read = twl4030_read_reg_cache;
 	codec->write = twl4030_write;
 	codec->set_bias_level = twl4030_set_bias_level;
+	codec->idle_bias_off = 1;
 	codec->dai = twl4030_dai;
 	codec->num_dai = ARRAY_SIZE(twl4030_dai);
 	codec->reg_cache_size = sizeof(twl4030_reg);
@@ -2302,9 +2340,7 @@
 
 	/* Set the defaults, and power up the codec */
 	twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
-	twl4030_init_chip(codec);
 	codec->bias_level = SND_SOC_BIAS_OFF;
-	twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
@@ -2322,7 +2358,7 @@
 	return 0;
 
 error_codec:
-	twl4030_power_down(codec);
+	twl4030_codec_enable(codec, 0);
 	kfree(codec->reg_cache);
 error_cache:
 	kfree(twl4030);
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index f206d24..6c57430 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -41,7 +41,11 @@
 
 struct twl4030_setup_data {
 	unsigned int ramp_delay_value;
+	unsigned int digimic_delay; /* in ms */
 	unsigned int sysclk;
+	unsigned int offset_cncl_path;
+	unsigned int check_defaults:1;
+	unsigned int reset_registers:1;
 	unsigned int hs_extmute:1;
 	void (*set_hs_extmute)(int mute);
 };
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index af36346..64a807f 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -360,6 +360,13 @@
 	return 0;
 }
 
+static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	msleep(1);
+	return 0;
+}
+
 static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
 			struct snd_kcontrol *kcontrol, int event)
 {
@@ -371,6 +378,8 @@
 	else
 		priv->non_lp--;
 
+	msleep(1);
+
 	return 0;
 }
 
@@ -471,20 +480,6 @@
 static const struct snd_kcontrol_new hfdacr_switch_controls =
 	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0);
 
-/* Headset driver switches */
-static const struct snd_kcontrol_new hsl_driver_switch_controls =
-	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 2, 1, 0);
-
-static const struct snd_kcontrol_new hsr_driver_switch_controls =
-	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 2, 1, 0);
-
-/* Handsfree driver switches */
-static const struct snd_kcontrol_new hfl_driver_switch_controls =
-	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 4, 1, 0);
-
-static const struct snd_kcontrol_new hfr_driver_switch_controls =
-	SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 4, 1, 0);
-
 static const struct snd_kcontrol_new ep_driver_switch_controls =
 	SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0);
 
@@ -548,10 +543,14 @@
 			TWL6040_REG_DMICBCTL, 4, 0),
 
 	/* DACs */
-	SND_SOC_DAPM_DAC("HSDAC Left", "Headset Playback",
-			TWL6040_REG_HSLCTL, 0, 0),
-	SND_SOC_DAPM_DAC("HSDAC Right", "Headset Playback",
-			TWL6040_REG_HSRCTL, 0, 0),
+	SND_SOC_DAPM_DAC_E("HSDAC Left", "Headset Playback",
+			TWL6040_REG_HSLCTL, 0, 0,
+			twl6040_hs_dac_event,
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("HSDAC Right", "Headset Playback",
+			TWL6040_REG_HSRCTL, 0, 0,
+			twl6040_hs_dac_event,
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 	SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback",
 			TWL6040_REG_HFLCTL, 0, 0,
 			twl6040_power_mode_event,
@@ -571,18 +570,19 @@
 	SND_SOC_DAPM_SWITCH("HFDAC Right Playback",
 			SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls),
 
-	SND_SOC_DAPM_SWITCH("Headset Left Driver",
-			SND_SOC_NOPM, 0, 0, &hsl_driver_switch_controls),
-	SND_SOC_DAPM_SWITCH("Headset Right Driver",
-			SND_SOC_NOPM, 0, 0, &hsr_driver_switch_controls),
-	SND_SOC_DAPM_SWITCH_E("Handsfree Left Driver",
-			SND_SOC_NOPM, 0, 0, &hfl_driver_switch_controls,
+	/* Analog playback drivers */
+	SND_SOC_DAPM_PGA_E("Handsfree Left Driver",
+			TWL6040_REG_HFLCTL, 4, 0, NULL, 0,
 			twl6040_power_mode_event,
 			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_SWITCH_E("Handsfree Right Driver",
-			SND_SOC_NOPM, 0, 0, &hfr_driver_switch_controls,
+	SND_SOC_DAPM_PGA_E("Handsfree Right Driver",
+			TWL6040_REG_HFRCTL, 4, 0, NULL, 0,
 			twl6040_power_mode_event,
 			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA("Headset Left Driver",
+			TWL6040_REG_HSLCTL, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Headset Right Driver",
+			TWL6040_REG_HSRCTL, 2, 0, NULL, 0),
 	SND_SOC_DAPM_SWITCH_E("Earphone Driver",
 			SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls,
 			twl6040_power_mode_event,
@@ -616,8 +616,8 @@
 	{"HSDAC Left Playback", "Switch", "HSDAC Left"},
 	{"HSDAC Right Playback", "Switch", "HSDAC Right"},
 
-	{"Headset Left Driver", "Switch", "HSDAC Left Playback"},
-	{"Headset Right Driver", "Switch", "HSDAC Right Playback"},
+	{"Headset Left Driver", NULL, "HSDAC Left Playback"},
+	{"Headset Right Driver", NULL, "HSDAC Right Playback"},
 
 	{"HSOL", NULL, "Headset Left Driver"},
 	{"HSOR", NULL, "Headset Right Driver"},
@@ -928,7 +928,7 @@
 		case 19200000:
 			/* mclk input, pll disabled */
 			hppllctl |= TWL6040_MCLK_19200KHZ |
-				    TWL6040_HPLLSQRBP |
+				    TWL6040_HPLLSQRENA |
 				    TWL6040_HPLLBP;
 			break;
 		case 26000000:
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index 28aac53..f3b4c1d 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -28,19 +28,6 @@
 #include "uda134x.h"
 
 
-#define POWER_OFF_ON_STANDBY 1
-/*
-  ALSA SOC usually puts the device in standby mode when it's not used
-  for sometime. If you define POWER_OFF_ON_STANDBY the driver will
-  turn off the ADC/DAC when this callback is invoked and turn it back
-  on when needed. Unfortunately this will result in a very light bump
-  (it can be audible only with good earphones). If this bothers you
-  just comment this line, you will have slightly higher power
-  consumption . Please note that sending the L3 command for ADC is
-  enough to make the bump, so it doesn't make difference if you
-  completely take off power from the codec.
- */
-
 #define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
 #define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
 		SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
@@ -58,7 +45,7 @@
 	/* Extended address registers */
 	0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
 	/* Status, data regs */
-	0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
+	0x00, 0x83, 0x00, 0x40, 0x80, 0xC0, 0x00,
 };
 
 /*
@@ -117,6 +104,7 @@
 	case UDA134X_DATA000:
 	case UDA134X_DATA001:
 	case UDA134X_DATA010:
+	case UDA134X_DATA011:
 		addr = UDA134X_DATA0_ADDR;
 		break;
 	case UDA134X_DATA1:
@@ -353,8 +341,22 @@
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		/* ADC, DAC on */
-		reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
-		uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
+		switch (pd->model) {
+		case UDA134X_UDA1340:
+		case UDA134X_UDA1344:
+		case UDA134X_UDA1345:
+			reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
+			uda134x_write(codec, UDA134X_DATA011, reg | 0x03);
+			break;
+		case UDA134X_UDA1341:
+			reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+			uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
+			break;
+		default:
+			printk(KERN_ERR "UDA134X SoC codec: "
+			       "unsupported model %d\n", pd->model);
+			return -EINVAL;
+		}
 		break;
 	case SND_SOC_BIAS_PREPARE:
 		/* power on */
@@ -367,8 +369,22 @@
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* ADC, DAC power off */
-		reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
-		uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
+		switch (pd->model) {
+		case UDA134X_UDA1340:
+		case UDA134X_UDA1344:
+		case UDA134X_UDA1345:
+			reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
+			uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03));
+			break;
+		case UDA134X_UDA1341:
+			reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+			uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
+			break;
+		default:
+			printk(KERN_ERR "UDA134X SoC codec: "
+			       "unsupported model %d\n", pd->model);
+			return -EINVAL;
+		}
 		break;
 	case SND_SOC_BIAS_OFF:
 		/* power off */
@@ -531,9 +547,7 @@
 	codec->num_dai = 1;
 	codec->read = uda134x_read_reg_cache;
 	codec->write = uda134x_write;
-#ifdef POWER_OFF_ON_STANDBY
-	codec->set_bias_level = uda134x_set_bias_level;
-#endif
+
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
@@ -544,6 +558,14 @@
 
 	uda134x_reset(codec);
 
+	if (pd->is_powered_on_standby) {
+		codec->set_bias_level = NULL;
+		uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
+	} else {
+		codec->set_bias_level = uda134x_set_bias_level;
+		uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	}
+
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h
index 94f4404..205f03b 100644
--- a/sound/soc/codecs/uda134x.h
+++ b/sound/soc/codecs/uda134x.h
@@ -23,9 +23,10 @@
 #define UDA134X_DATA000 10
 #define UDA134X_DATA001 11
 #define UDA134X_DATA010 12
-#define UDA134X_DATA1	13
+#define UDA134X_DATA011 13
+#define UDA134X_DATA1   14
 
-#define UDA134X_REGS_NUM 14
+#define UDA134X_REGS_NUM 15
 
 #define STATUS0_DAIFMT_MASK (~(7<<1))
 #define STATUS0_SYSCLK_MASK (~(3<<4))
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index 002e289..4bcd168 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -795,6 +795,8 @@
 
 	dev_set_drvdata(&i2c->dev, wm2000);
 	wm2000->anc_eng_ena = 1;
+	wm2000->anc_active = 1;
+	wm2000->spk_ena = 1;
 	wm2000->i2c = i2c;
 
 	wm2000_reset(wm2000);
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 37242a7..0ad039b 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -482,7 +482,8 @@
 
 	if (wm8523_codec) {
 		dev_err(codec->dev, "Another WM8523 is registered\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	mutex_init(&codec->mutex);
@@ -570,18 +571,19 @@
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		return ret;
+		goto err_enable;
 	}
 
 	ret = snd_soc_register_dai(&wm8523_dai);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
-		snd_soc_unregister_codec(codec);
-		return ret;
+		goto err_codec;
 	}
 
 	return 0;
 
+err_codec:
+	snd_soc_unregister_codec(codec);
 err_enable:
 	regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
 err_get:
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index effb14e..e2dba07f 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -439,7 +439,8 @@
 
 	if (wm8711_codec) {
 		dev_err(codec->dev, "Another WM8711 is registered\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	mutex_init(&codec->mutex);
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
new file mode 100644
index 0000000..b9ea890
--- /dev/null
+++ b/sound/soc/codecs/wm8741.c
@@ -0,0 +1,579 @@
+/*
+ * wm8741.c  --  WM8741 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Wolfson Microelectronics plc
+ *
+ * Author: Ian Lartey <ian@opensource.wolfsonmicro.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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 <linux/regulator/consumer.h>
+#include <linux/slab.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 "wm8741.h"
+
+static struct snd_soc_codec *wm8741_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8741;
+
+#define WM8741_NUM_SUPPLIES 2
+static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
+	"AVDD",
+	"DVDD",
+};
+
+#define WM8741_NUM_RATES 4
+
+/* codec private data */
+struct wm8741_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8741_REGISTER_COUNT];
+	struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
+	unsigned int sysclk;
+	unsigned int rate_constraint_list[WM8741_NUM_RATES];
+	struct snd_pcm_hw_constraint_list rate_constraint;
+};
+
+static const u16 wm8741_reg_defaults[WM8741_REGISTER_COUNT] = {
+	0x0000,     /* R0  - DACLLSB Attenuation */
+	0x0000,     /* R1  - DACLMSB Attenuation */
+	0x0000,     /* R2  - DACRLSB Attenuation */
+	0x0000,     /* R3  - DACRMSB Attenuation */
+	0x0000,     /* R4  - Volume Control */
+	0x000A,     /* R5  - Format Control */
+	0x0000,     /* R6  - Filter Control */
+	0x0000,     /* R7  - Mode Control 1 */
+	0x0002,     /* R8  - Mode Control 2 */
+	0x0000,	    /* R9  - Reset */
+	0x0002,     /* R32 - ADDITONAL_CONTROL_1 */
+};
+
+
+static int wm8741_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, WM8741_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
+
+static const struct snd_kcontrol_new wm8741_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
+		 WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
+SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
+		 WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
+};
+
+static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("VOUTLP"),
+SND_SOC_DAPM_OUTPUT("VOUTLN"),
+SND_SOC_DAPM_OUTPUT("VOUTRP"),
+SND_SOC_DAPM_OUTPUT("VOUTRN"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{ "VOUTLP", NULL, "DACL" },
+	{ "VOUTLN", NULL, "DACL" },
+	{ "VOUTRP", NULL, "DACR" },
+	{ "VOUTRN", NULL, "DACR" },
+};
+
+static int wm8741_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8741_dapm_widgets,
+				  ARRAY_SIZE(wm8741_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	return 0;
+}
+
+static struct {
+	int value;
+	int ratio;
+} lrclk_ratios[WM8741_NUM_RATES] = {
+	{ 1, 256 },
+	{ 2, 384 },
+	{ 3, 512 },
+	{ 4, 768 },
+};
+
+
+static int wm8741_startup(struct snd_pcm_substream *substream,
+			  struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+	/* The set of sample rates that can be supported depends on the
+	 * MCLK supplied to the CODEC - enforce this.
+	 */
+	if (!wm8741->sysclk) {
+		dev_err(codec->dev,
+			"No MCLK configured, call set_sysclk() on init\n");
+		return -EINVAL;
+	}
+
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_RATE,
+				   &wm8741->rate_constraint);
+
+	return 0;
+}
+
+static int wm8741_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+	u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
+	int i;
+
+	/* Find a supported LRCLK ratio */
+	for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+		if (wm8741->sysclk / params_rate(params) ==
+		    lrclk_ratios[i].ratio)
+			break;
+	}
+
+	/* Should never happen, should be handled by constraints */
+	if (i == ARRAY_SIZE(lrclk_ratios)) {
+		dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
+			wm8741->sysclk / params_rate(params));
+		return -EINVAL;
+	}
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0001;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0002;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x0003;
+		break;
+	default:
+		dev_dbg(codec->dev, "wm8741_hw_params:    Unsupported bit size param = %d",
+			params_format(params));
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev, "wm8741_hw_params:    bit size param = %d",
+		params_format(params));
+
+	snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
+	return 0;
+}
+
+static int wm8741_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 wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val;
+	int i;
+
+	dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
+
+	wm8741->sysclk = freq;
+
+	wm8741->rate_constraint.count = 0;
+
+	for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+		dev_dbg(codec->dev, "index = %d, ratio = %d, freq = %d",
+				i, lrclk_ratios[i].ratio, freq);
+
+		val = freq / lrclk_ratios[i].ratio;
+		/* Check that it's a standard rate since core can't
+		 * cope with others and having the odd rates confuses
+		 * constraint matching.
+		 */
+		switch (val) {
+		case 32000:
+		case 44100:
+		case 48000:
+		case 64000:
+		case 88200:
+		case 96000:
+			dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
+				val);
+			wm8741->rate_constraint_list[i] = val;
+			wm8741->rate_constraint.count++;
+			break;
+		default:
+			dev_dbg(codec->dev, "Skipping sample rate: %dHz\n",
+				val);
+		}
+	}
+
+	/* Need at least one supported rate... */
+	if (wm8741->rate_constraint.count == 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3;
+
+	/* check master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	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 |= 0x0008;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0004;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x0013;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x0010;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0020;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0030;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+
+	dev_dbg(codec->dev, "wm8741_set_dai_fmt:    Format=%x, Clock Inv=%x\n",
+				fmt & SND_SOC_DAIFMT_FORMAT_MASK,
+				((fmt & SND_SOC_DAIFMT_INV_MASK)));
+
+	snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
+	return 0;
+}
+
+#define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+			SNDRV_PCM_RATE_192000)
+
+#define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8741_dai_ops = {
+	.startup	= wm8741_startup,
+	.hw_params	= wm8741_hw_params,
+	.set_sysclk	= wm8741_set_dai_sysclk,
+	.set_fmt	= wm8741_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8741_dai = {
+	.name = "WM8741",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,  /* Mono modes not yet supported */
+		.channels_max = 2,
+		.rates = WM8741_RATES,
+		.formats = WM8741_FORMATS,
+	},
+	.ops = &wm8741_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8741_dai);
+
+#ifdef CONFIG_PM
+static int wm8741_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u16 *cache = codec->reg_cache;
+	int i;
+
+	/* RESTORE REG Cache */
+	for (i = 0; i < WM8741_REGISTER_COUNT; i++) {
+		if (cache[i] == wm8741_reg_defaults[i] || WM8741_RESET == i)
+			continue;
+		snd_soc_write(codec, i, cache[i]);
+	}
+	return 0;
+}
+#else
+#define wm8741_suspend NULL
+#define wm8741_resume NULL
+#endif
+
+static int wm8741_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8741_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8741_codec;
+	codec = wm8741_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm8741_snd_controls,
+			     ARRAY_SIZE(wm8741_snd_controls));
+	wm8741_add_widgets(codec);
+
+	return ret;
+
+pcm_err:
+	return ret;
+}
+
+static int wm8741_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8741 = {
+	.probe =	wm8741_probe,
+	.remove =	wm8741_remove,
+	.resume =	wm8741_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8741);
+
+static int wm8741_register(struct wm8741_priv *wm8741,
+			   enum snd_soc_control_type control)
+{
+	int ret;
+	struct snd_soc_codec *codec = &wm8741->codec;
+	int i;
+
+	if (wm8741_codec) {
+		dev_err(codec->dev, "Another WM8741 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	snd_soc_codec_set_drvdata(codec, wm8741);
+	codec->name = "WM8741";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = NULL;
+	codec->dai = &wm8741_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8741_REGISTER_COUNT;
+	codec->reg_cache = &wm8741->reg_cache;
+
+	wm8741->rate_constraint.list = &wm8741->rate_constraint_list[0];
+	wm8741->rate_constraint.count =
+		ARRAY_SIZE(wm8741->rate_constraint_list);
+
+	memcpy(codec->reg_cache, wm8741_reg_defaults,
+		sizeof(wm8741->reg_cache));
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
+		wm8741->supplies[i].supply = wm8741_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8741->supplies),
+				 wm8741->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
+				    wm8741->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_get;
+	}
+
+	ret = wm8741_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err_enable;
+	}
+
+	wm8741_dai.dev = codec->dev;
+
+	/* Change some default settings - latch VU */
+	wm8741->reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
+	wm8741->reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
+	wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
+	wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
+
+	wm8741_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&wm8741_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	dev_dbg(codec->dev, "Successful registration\n");
+	return 0;
+
+err_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+
+err_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+
+err:
+	kfree(wm8741);
+	return ret;
+}
+
+static void wm8741_unregister(struct wm8741_priv *wm8741)
+{
+	regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+
+	snd_soc_unregister_dai(&wm8741_dai);
+	snd_soc_unregister_codec(&wm8741->codec);
+	kfree(wm8741);
+	wm8741_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8741_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8741_priv *wm8741;
+	struct snd_soc_codec *codec;
+
+	wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL);
+	if (wm8741 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8741->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, wm8741);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8741_register(wm8741, SND_SOC_I2C);
+}
+
+static __devexit int wm8741_i2c_remove(struct i2c_client *client)
+{
+	struct wm8741_priv *wm8741 = i2c_get_clientdata(client);
+	wm8741_unregister(wm8741);
+	return 0;
+}
+
+static const struct i2c_device_id wm8741_i2c_id[] = {
+	{ "wm8741", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
+
+
+static struct i2c_driver wm8741_i2c_driver = {
+	.driver = {
+		.name = "WM8741",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8741_i2c_probe,
+	.remove =   __devexit_p(wm8741_i2c_remove),
+	.id_table = wm8741_i2c_id,
+};
+#endif
+
+static int __init wm8741_modinit(void)
+{
+	int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8741_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8741 I2C driver: %d\n",
+		       ret);
+	}
+#endif
+	return 0;
+}
+module_init(wm8741_modinit);
+
+static void __exit wm8741_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8741_i2c_driver);
+#endif
+}
+module_exit(wm8741_exit);
+
+MODULE_DESCRIPTION("ASoC WM8741 driver");
+MODULE_AUTHOR("Ian Lartey <ian@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h
new file mode 100644
index 0000000..fdef6ec
--- /dev/null
+++ b/sound/soc/codecs/wm8741.h
@@ -0,0 +1,214 @@
+/*
+ * wm8741.h  --  WM8423 ASoC driver
+ *
+ * Copyright 2010 Wolfson Microelectronics, plc
+ *
+ * Author: Ian Lartey <ian@opensource.wolfsonmicro.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 _WM8741_H
+#define _WM8741_H
+
+/*
+ * Register values.
+ */
+#define WM8741_DACLLSB_ATTENUATION              0x00
+#define WM8741_DACLMSB_ATTENUATION              0x01
+#define WM8741_DACRLSB_ATTENUATION              0x02
+#define WM8741_DACRMSB_ATTENUATION              0x03
+#define WM8741_VOLUME_CONTROL                   0x04
+#define WM8741_FORMAT_CONTROL                   0x05
+#define WM8741_FILTER_CONTROL                   0x06
+#define WM8741_MODE_CONTROL_1                   0x07
+#define WM8741_MODE_CONTROL_2                   0x08
+#define WM8741_RESET                            0x09
+#define WM8741_ADDITIONAL_CONTROL_1             0x20
+
+#define WM8741_REGISTER_COUNT                   11
+#define WM8741_MAX_REGISTER                     0x20
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - DACLLSB_ATTENUATION
+ */
+#define WM8741_UPDATELL                         0x0020  /* UPDATELL */
+#define WM8741_UPDATELL_MASK                    0x0020  /* UPDATELL */
+#define WM8741_UPDATELL_SHIFT                        5  /* UPDATELL */
+#define WM8741_UPDATELL_WIDTH                        1  /* UPDATELL */
+#define WM8741_LAT_4_0_MASK                     0x001F  /* LAT[4:0] - [4:0] */
+#define WM8741_LAT_4_0_SHIFT                         0  /* LAT[4:0] - [4:0] */
+#define WM8741_LAT_4_0_WIDTH                         5  /* LAT[4:0] - [4:0] */
+
+/*
+ * R1 (0x01) - DACLMSB_ATTENUATION
+ */
+#define WM8741_UPDATELM                         0x0020  /* UPDATELM */
+#define WM8741_UPDATELM_MASK                    0x0020  /* UPDATELM */
+#define WM8741_UPDATELM_SHIFT                        5  /* UPDATELM */
+#define WM8741_UPDATELM_WIDTH                        1  /* UPDATELM */
+#define WM8741_LAT_9_5_0_MASK                   0x001F  /* LAT[9:5] - [4:0] */
+#define WM8741_LAT_9_5_0_SHIFT                       0  /* LAT[9:5] - [4:0] */
+#define WM8741_LAT_9_5_0_WIDTH                       5  /* LAT[9:5] - [4:0] */
+
+/*
+ * R2 (0x02) - DACRLSB_ATTENUATION
+ */
+#define WM8741_UPDATERL                         0x0020  /* UPDATERL */
+#define WM8741_UPDATERL_MASK                    0x0020  /* UPDATERL */
+#define WM8741_UPDATERL_SHIFT                        5  /* UPDATERL */
+#define WM8741_UPDATERL_WIDTH                        1  /* UPDATERL */
+#define WM8741_RAT_4_0_MASK                     0x001F  /* RAT[4:0] - [4:0] */
+#define WM8741_RAT_4_0_SHIFT                         0  /* RAT[4:0] - [4:0] */
+#define WM8741_RAT_4_0_WIDTH                         5  /* RAT[4:0] - [4:0] */
+
+/*
+ * R3 (0x03) - DACRMSB_ATTENUATION
+ */
+#define WM8741_UPDATERM                         0x0020  /* UPDATERM */
+#define WM8741_UPDATERM_MASK                    0x0020  /* UPDATERM */
+#define WM8741_UPDATERM_SHIFT                        5  /* UPDATERM */
+#define WM8741_UPDATERM_WIDTH                        1  /* UPDATERM */
+#define WM8741_RAT_9_5_0_MASK                   0x001F  /* RAT[9:5] - [4:0] */
+#define WM8741_RAT_9_5_0_SHIFT                       0  /* RAT[9:5] - [4:0] */
+#define WM8741_RAT_9_5_0_WIDTH                       5  /* RAT[9:5] - [4:0] */
+
+/*
+ * R4 (0x04) - VOLUME_CONTROL
+ */
+#define WM8741_AMUTE                            0x0080  /* AMUTE */
+#define WM8741_AMUTE_MASK                       0x0080  /* AMUTE */
+#define WM8741_AMUTE_SHIFT                           7  /* AMUTE */
+#define WM8741_AMUTE_WIDTH                           1  /* AMUTE */
+#define WM8741_ZFLAG_MASK                       0x0060  /* ZFLAG - [6:5] */
+#define WM8741_ZFLAG_SHIFT                           5  /* ZFLAG - [6:5] */
+#define WM8741_ZFLAG_WIDTH                           2  /* ZFLAG - [6:5] */
+#define WM8741_IZD                              0x0010  /* IZD */
+#define WM8741_IZD_MASK                         0x0010  /* IZD */
+#define WM8741_IZD_SHIFT                             4  /* IZD */
+#define WM8741_IZD_WIDTH                             1  /* IZD */
+#define WM8741_SOFT                             0x0008  /* SOFT MUTE */
+#define WM8741_SOFT_MASK                        0x0008  /* SOFT MUTE */
+#define WM8741_SOFT_SHIFT                            3  /* SOFT MUTE */
+#define WM8741_SOFT_WIDTH                            1  /* SOFT MUTE */
+#define WM8741_ATC                              0x0004  /* ATC */
+#define WM8741_ATC_MASK                         0x0004  /* ATC */
+#define WM8741_ATC_SHIFT                             2  /* ATC */
+#define WM8741_ATC_WIDTH                             1  /* ATC */
+#define WM8741_ATT2DB                           0x0002  /* ATT2DB */
+#define WM8741_ATT2DB_MASK                      0x0002  /* ATT2DB */
+#define WM8741_ATT2DB_SHIFT                          1  /* ATT2DB */
+#define WM8741_ATT2DB_WIDTH                          1  /* ATT2DB */
+#define WM8741_VOL_RAMP                         0x0001  /* VOL_RAMP */
+#define WM8741_VOL_RAMP_MASK                    0x0001  /* VOL_RAMP */
+#define WM8741_VOL_RAMP_SHIFT                        0  /* VOL_RAMP */
+#define WM8741_VOL_RAMP_WIDTH                        1  /* VOL_RAMP */
+
+/*
+ * R5 (0x05) - FORMAT_CONTROL
+ */
+#define WM8741_PWDN                             0x0080  /* PWDN */
+#define WM8741_PWDN_MASK                        0x0080  /* PWDN */
+#define WM8741_PWDN_SHIFT                            7  /* PWDN */
+#define WM8741_PWDN_WIDTH                            1  /* PWDN */
+#define WM8741_REV                              0x0040  /* REV */
+#define WM8741_REV_MASK                         0x0040  /* REV */
+#define WM8741_REV_SHIFT                             6  /* REV */
+#define WM8741_REV_WIDTH                             1  /* REV */
+#define WM8741_BCP                              0x0020  /* BCP */
+#define WM8741_BCP_MASK                         0x0020  /* BCP */
+#define WM8741_BCP_SHIFT                             5  /* BCP */
+#define WM8741_BCP_WIDTH                             1  /* BCP */
+#define WM8741_LRP                              0x0010  /* LRP */
+#define WM8741_LRP_MASK                         0x0010  /* LRP */
+#define WM8741_LRP_SHIFT                             4  /* LRP */
+#define WM8741_LRP_WIDTH                             1  /* LRP */
+#define WM8741_FMT_MASK                         0x000C  /* FMT - [3:2] */
+#define WM8741_FMT_SHIFT                             2  /* FMT - [3:2] */
+#define WM8741_FMT_WIDTH                             2  /* FMT - [3:2] */
+#define WM8741_IWL_MASK                         0x0003  /* IWL - [1:0] */
+#define WM8741_IWL_SHIFT                             0  /* IWL - [1:0] */
+#define WM8741_IWL_WIDTH                             2  /* IWL - [1:0] */
+
+/*
+ * R6 (0x06) - FILTER_CONTROL
+ */
+#define WM8741_ZFLAG_HI                         0x0080  /* ZFLAG_HI */
+#define WM8741_ZFLAG_HI_MASK                    0x0080  /* ZFLAG_HI */
+#define WM8741_ZFLAG_HI_SHIFT                        7  /* ZFLAG_HI */
+#define WM8741_ZFLAG_HI_WIDTH                        1  /* ZFLAG_HI */
+#define WM8741_DEEMPH_MASK                      0x0060  /* DEEMPH - [6:5] */
+#define WM8741_DEEMPH_SHIFT                          5  /* DEEMPH - [6:5] */
+#define WM8741_DEEMPH_WIDTH                          2  /* DEEMPH - [6:5] */
+#define WM8741_DSDFILT_MASK                     0x0018  /* DSDFILT - [4:3] */
+#define WM8741_DSDFILT_SHIFT                         3  /* DSDFILT - [4:3] */
+#define WM8741_DSDFILT_WIDTH                         2  /* DSDFILT - [4:3] */
+#define WM8741_FIRSEL_MASK                      0x0007  /* FIRSEL - [2:0] */
+#define WM8741_FIRSEL_SHIFT                          0  /* FIRSEL - [2:0] */
+#define WM8741_FIRSEL_WIDTH                          3  /* FIRSEL - [2:0] */
+
+/*
+ * R7 (0x07) - MODE_CONTROL_1
+ */
+#define WM8741_MODE8X                           0x0080  /* MODE8X */
+#define WM8741_MODE8X_MASK                      0x0080  /* MODE8X */
+#define WM8741_MODE8X_SHIFT                          7  /* MODE8X */
+#define WM8741_MODE8X_WIDTH                          1  /* MODE8X */
+#define WM8741_OSR_MASK                         0x0060  /* OSR - [6:5] */
+#define WM8741_OSR_SHIFT                             5  /* OSR - [6:5] */
+#define WM8741_OSR_WIDTH                             2  /* OSR - [6:5] */
+#define WM8741_SR_MASK                          0x001C  /* SR - [4:2] */
+#define WM8741_SR_SHIFT                              2  /* SR - [4:2] */
+#define WM8741_SR_WIDTH                              3  /* SR - [4:2] */
+#define WM8741_MODESEL_MASK                     0x0003  /* MODESEL - [1:0] */
+#define WM8741_MODESEL_SHIFT                         0  /* MODESEL - [1:0] */
+#define WM8741_MODESEL_WIDTH                         2  /* MODESEL - [1:0] */
+
+/*
+ * R8 (0x08) - MODE_CONTROL_2
+ */
+#define WM8741_DSD_GAIN                         0x0040  /* DSD_GAIN */
+#define WM8741_DSD_GAIN_MASK                    0x0040  /* DSD_GAIN */
+#define WM8741_DSD_GAIN_SHIFT                        6  /* DSD_GAIN */
+#define WM8741_DSD_GAIN_WIDTH                        1  /* DSD_GAIN */
+#define WM8741_SDOUT                            0x0020  /* SDOUT */
+#define WM8741_SDOUT_MASK                       0x0020  /* SDOUT */
+#define WM8741_SDOUT_SHIFT                           5  /* SDOUT */
+#define WM8741_SDOUT_WIDTH                           1  /* SDOUT */
+#define WM8741_DOUT                             0x0010  /* DOUT */
+#define WM8741_DOUT_MASK                        0x0010  /* DOUT */
+#define WM8741_DOUT_SHIFT                            4  /* DOUT */
+#define WM8741_DOUT_WIDTH                            1  /* DOUT */
+#define WM8741_DIFF_MASK                        0x000C  /* DIFF - [3:2] */
+#define WM8741_DIFF_SHIFT                            2  /* DIFF - [3:2] */
+#define WM8741_DIFF_WIDTH                            2  /* DIFF - [3:2] */
+#define WM8741_DITHER_MASK                      0x0003  /* DITHER - [1:0] */
+#define WM8741_DITHER_SHIFT                          0  /* DITHER - [1:0] */
+#define WM8741_DITHER_WIDTH                          2  /* DITHER - [1:0] */
+
+/*
+ * R32 (0x20) - ADDITONAL_CONTROL_1
+ */
+#define WM8741_DSD_LEVEL                        0x0002  /* DSD_LEVEL */
+#define WM8741_DSD_LEVEL_MASK                   0x0002  /* DSD_LEVEL */
+#define WM8741_DSD_LEVEL_SHIFT                       1  /* DSD_LEVEL */
+#define WM8741_DSD_LEVEL_WIDTH                       1  /* DSD_LEVEL */
+#define WM8741_DSD_NO_NOTCH                     0x0001  /* DSD_NO_NOTCH */
+#define WM8741_DSD_NO_NOTCH_MASK                0x0001  /* DSD_NO_NOTCH */
+#define WM8741_DSD_NO_NOTCH_SHIFT                    0  /* DSD_NO_NOTCH */
+#define WM8741_DSD_NO_NOTCH_WIDTH                    1  /* DSD_NO_NOTCH */
+
+#define  WM8741_SYSCLK 0
+
+extern struct snd_soc_dai wm8741_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8741;
+
+#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 9407e19..e2c05e3 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -884,6 +884,7 @@
 
 static const struct i2c_device_id wm8750_i2c_id[] = {
 	{ "wm8750", 0 },
+	{ "wm8987", 0 }, /* WM8987 is register compatible with WM8750 */
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
@@ -925,14 +926,22 @@
 	return 0;
 }
 
+static const struct spi_device_id wm8750_spi_id[] = {
+	{ "wm8750", 0 },
+	{ "wm8987", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, wm8750_spi_id);
+
 static struct spi_driver wm8750_spi_driver = {
 	.driver = {
-		.name	= "wm8750",
+		.name	= "WM8750 SPI Codec",
 		.bus	= &spi_bus_type,
 		.owner	= THIS_MODULE,
 	},
 	.probe		= wm8750_spi_probe,
 	.remove		= __devexit_p(wm8750_spi_remove),
+	.id_table	= wm8750_spi_id,
 };
 #endif
 
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 87f14f8..f7dcabf 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2433,7 +2433,8 @@
 
 	if (wm8904_codec) {
 		dev_err(codec->dev, "Another WM8904 is registered\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	mutex_init(&codec->mutex);
@@ -2462,7 +2463,8 @@
 	default:
 		dev_err(codec->dev, "Unknown device type %d\n",
 			wm8904->devtype);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	memcpy(codec->reg_cache, wm8904_reg, sizeof(wm8904_reg));
@@ -2566,18 +2568,19 @@
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		return ret;
+		goto err_enable;
 	}
 
 	ret = snd_soc_register_dai(&wm8904_dai);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
-		snd_soc_unregister_codec(codec);
-		return ret;
+		goto err_codec;
 	}
 
 	return 0;
 
+err_codec:
+	snd_soc_unregister_codec(codec);
 err_enable:
 	regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
 err_get:
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index e3c4bbf..f0c1113 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -845,6 +845,7 @@
 static int wm8940_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
 {
+	int ret;
 	struct wm8940_priv *wm8940;
 	struct snd_soc_codec *codec;
 
@@ -858,7 +859,11 @@
 	codec->control_data = i2c;
 	codec->dev = &i2c->dev;
 
-	return wm8940_register(wm8940, SND_SOC_I2C);
+	ret = wm8940_register(wm8940, SND_SOC_I2C);
+	if (ret < 0)
+		kfree(wm8940);
+
+	return ret;
 }
 
 static int __devexit wm8940_i2c_remove(struct i2c_client *client)
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index fedb764..5f02559 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -964,7 +964,8 @@
 
 	if (wm8955_codec) {
 		dev_err(codec->dev, "Another WM8955 is registered\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	mutex_init(&codec->mutex);
@@ -1047,18 +1048,19 @@
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		return ret;
+		goto err_enable;
 	}
 
 	ret = snd_soc_register_dai(&wm8955_dai);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
-		snd_soc_unregister_codec(codec);
-		return ret;
+		goto err_codec;
 	}
 
 	return 0;
 
+err_codec:
+	snd_soc_unregister_codec(codec);
 err_enable:
 	regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
 err_get:
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 7233cc6..3c6ee61 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -79,12 +79,13 @@
 	struct snd_soc_dapm_widget *lout1;
 	struct snd_soc_dapm_widget *rout1;
 	struct snd_soc_dapm_widget *out3;
+	bool deemph;
+	int playback_fs;
 };
 
 #define wm8960_reset(c)	snd_soc_write(c, WM8960_RESET, 0)
 
 /* enumerated controls */
-static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
 static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
 	"Right Inverted", "Stereo Inversion"};
 static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
@@ -93,7 +94,6 @@
 static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
 
 static const struct soc_enum wm8960_enum[] = {
-	SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
 	SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
 	SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
 	SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
@@ -102,6 +102,59 @@
 	SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
 };
 
+static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8960_set_deemph(struct snd_soc_codec *codec)
+{
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+	int val, i, best;
+
+	/* If we're using deemphasis select the nearest available sample
+	 * rate.
+	 */
+	if (wm8960->deemph) {
+		best = 1;
+		for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+			if (abs(deemph_settings[i] - wm8960->playback_fs) <
+			    abs(deemph_settings[best] - wm8960->playback_fs))
+				best = i;
+		}
+
+		val = best << 1;
+	} else {
+		val = 0;
+	}
+
+	dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+	return snd_soc_update_bits(codec, WM8960_DACCTL1,
+				   0x6, val);
+}
+
+static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+	return wm8960->deemph;
+}
+
+static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+	int deemph = ucontrol->value.enumerated.item[0];
+
+	if (deemph > 1)
+		return -EINVAL;
+
+	wm8960->deemph = deemph;
+
+	return wm8960_set_deemph(codec);
+}
+
 static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
 static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
 static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
@@ -131,23 +184,24 @@
 SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
 
 SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
-SOC_ENUM("ADC Polarity", wm8960_enum[1]),
-SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
+SOC_ENUM("ADC Polarity", wm8960_enum[0]),
 SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
 
 SOC_ENUM("DAC Polarity", wm8960_enum[2]),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+		    wm8960_get_deemph, wm8960_put_deemph),
 
-SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
-SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
+SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]),
+SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]),
 SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
 SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
 
-SOC_ENUM("ALC Function", wm8960_enum[5]),
+SOC_ENUM("ALC Function", wm8960_enum[4]),
 SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
 SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
 SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
 SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
-SOC_ENUM("ALC Mode", wm8960_enum[6]),
+SOC_ENUM("ALC Mode", wm8960_enum[5]),
 SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
 SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
 
@@ -433,6 +487,21 @@
 	return 0;
 }
 
+static struct {
+	int rate;
+	unsigned int val;
+} alc_rates[] = {
+	{ 48000, 0 },
+	{ 44100, 0 },
+	{ 32000, 1 },
+	{ 22050, 2 },
+	{ 24000, 2 },
+	{ 16000, 3 },
+	{ 11250, 4 },
+	{ 12000, 4 },
+	{  8000, 5 },
+};
+
 static int wm8960_hw_params(struct snd_pcm_substream *substream,
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
@@ -440,7 +509,9 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
 	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
+	int i;
 
 	/* bit size */
 	switch (params_format(params)) {
@@ -454,6 +525,18 @@
 		break;
 	}
 
+	/* Update filters for the new rate */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		wm8960->playback_fs = params_rate(params);
+		wm8960_set_deemph(codec);
+	} else {
+		for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
+			if (alc_rates[i].rate == params_rate(params))
+				snd_soc_update_bits(codec,
+						    WM8960_ADDCTL3, 0x7,
+						    alc_rates[i].val);
+	}
+
 	/* set iface */
 	snd_soc_write(codec, WM8960_IFACE1, iface);
 	return 0;
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 5b9a756..2549d3a 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -1102,7 +1102,7 @@
 	ret = wm8961_reset(codec);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to issue reset\n");
-		return ret;
+		goto err;
 	}
 
 	/* Enable class W */
@@ -1147,18 +1147,19 @@
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		return ret;
+		goto err;
 	}
 
 	ret = snd_soc_register_dai(&wm8961_dai);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
-		snd_soc_unregister_codec(codec);
-		return ret;
+		goto err_codec;
 	}
 
 	return 0;
 
+err_codec:
+	snd_soc_unregister_codec(codec);
 err:
 	kfree(wm8961);
 	return ret;
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index a2c4b2f..1468fe1 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -670,7 +670,8 @@
 
 	if (wm8974_codec) {
 		dev_err(codec->dev, "Another WM8974 is registered\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	mutex_init(&codec->mutex);
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 51d5f43..8a1ad77 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1076,7 +1076,6 @@
 err_codec:
 	snd_soc_unregister_codec(codec);
 err:
-	kfree(wm8978);
 	return ret;
 }
 
@@ -1085,13 +1084,13 @@
 	wm8978_set_bias_level(&wm8978->codec, SND_SOC_BIAS_OFF);
 	snd_soc_unregister_dai(&wm8978_dai);
 	snd_soc_unregister_codec(&wm8978->codec);
-	kfree(wm8978);
 	wm8978_codec = NULL;
 }
 
 static __devinit int wm8978_i2c_probe(struct i2c_client *i2c,
 				      const struct i2c_device_id *id)
 {
+	int ret;
 	struct wm8978_priv *wm8978;
 	struct snd_soc_codec *codec;
 
@@ -1107,13 +1106,18 @@
 
 	codec->dev = &i2c->dev;
 
-	return wm8978_register(wm8978);
+	ret = wm8978_register(wm8978);
+	if (ret < 0)
+		kfree(wm8978);
+
+	return ret;
 }
 
 static __devexit int wm8978_i2c_remove(struct i2c_client *client)
 {
 	struct wm8978_priv *wm8978 = i2c_get_clientdata(client);
 	wm8978_unregister(wm8978);
+	kfree(wm8978);
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index c018772..dd8d909 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -30,8 +30,6 @@
 
 #include "wm8990.h"
 
-#define WM8990_VERSION "0.2"
-
 /* codec private data */
 struct wm8990_priv {
 	unsigned int sysclk;
@@ -1511,8 +1509,6 @@
 	struct wm8990_priv *wm8990;
 	int ret;
 
-	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)
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index e84a117..a87046a 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -1677,6 +1677,26 @@
 
 static int wm8994_readable(unsigned int reg)
 {
+	switch (reg) {
+	case WM8994_GPIO_1:
+	case WM8994_GPIO_2:
+	case WM8994_GPIO_3:
+	case WM8994_GPIO_4:
+	case WM8994_GPIO_5:
+	case WM8994_GPIO_6:
+	case WM8994_GPIO_7:
+	case WM8994_GPIO_8:
+	case WM8994_GPIO_9:
+	case WM8994_GPIO_10:
+	case WM8994_GPIO_11:
+	case WM8994_INTERRUPT_STATUS_1:
+	case WM8994_INTERRUPT_STATUS_2:
+	case WM8994_INTERRUPT_RAW_STATUS_2:
+		return 1;
+	default:
+		break;
+	}
+
 	if (reg >= ARRAY_SIZE(access_masks))
 		return 0;
 	return access_masks[reg].readable != 0;
@@ -2341,6 +2361,20 @@
 		0, 1, 0),
 };
 
+static const struct snd_kcontrol_new aif1adc2l_mix[] = {
+SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING,
+		1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING,
+		0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc2r_mix[] = {
+SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING,
+		1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING,
+		0, 1, 0),
+};
+
 static const struct snd_kcontrol_new aif2dac2l_mix[] = {
 SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
 		5, 1, 0),
@@ -2472,6 +2506,7 @@
 static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
 SND_SOC_DAPM_INPUT("DMIC1DAT"),
 SND_SOC_DAPM_INPUT("DMIC2DAT"),
+SND_SOC_DAPM_INPUT("Clock"),
 
 SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
 		    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -2506,6 +2541,11 @@
 SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0,
 		   aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)),
 
+SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0,
+		   aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)),
+SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0,
+		   aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)),
+
 SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0,
 		   aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)),
 SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0,
@@ -2668,6 +2708,14 @@
 	{ "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" },
 	{ "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" },
 
+	{ "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" },
+	{ "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" },
+	{ "AIF1ADC2L Mixer", "AIF2 Switch", "AIF2DACL" },
+
+	{ "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" },
+	{ "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" },
+	{ "AIF1ADC2R Mixer", "AIF2 Switch", "AIF2DACR" },
+
 	/* Pin level routing for AIF3 */
 	{ "AIF1DAC1L", NULL, "AIF1DAC Mux" },
 	{ "AIF1DAC1R", NULL, "AIF1DAC Mux" },
@@ -2946,11 +2994,14 @@
 	return 0;
 }
 
+static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
+
 static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+	int i;
 
 	switch (dai->id) {
 	case 1:
@@ -2988,6 +3039,25 @@
 		dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id);
 		break;
 
+	case WM8994_SYSCLK_OPCLK:
+		/* Special case - a division (times 10) is given and
+		 * no effect on main clocking. 
+		 */
+		if (freq) {
+			for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
+				if (opclk_divs[i] == freq)
+					break;
+			if (i == ARRAY_SIZE(opclk_divs))
+				return -EINVAL;
+			snd_soc_update_bits(codec, WM8994_CLOCKING_2,
+					    WM8994_OPCLK_DIV_MASK, i);
+			snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+					    WM8994_OPCLK_ENA, WM8994_OPCLK_ENA);
+		} else {
+			snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
+					    WM8994_OPCLK_ENA, 0);
+		}
+
 	default:
 		return -EINVAL;
 	}
@@ -4004,6 +4074,11 @@
 			    1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
 			    1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
 
+	/* Unconditionally enable AIF1 ADC TDM mode; it only affects
+	 * behaviour on idle TDM clock cycles. */
+	snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
+			    WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
+
 	wm8994_update_class_w(codec);
 
 	ret = snd_soc_register_codec(codec);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 7072dc5..2e0ca67 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -20,6 +20,9 @@
 #define WM8994_SYSCLK_FLL1  3
 #define WM8994_SYSCLK_FLL2  4
 
+/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */
+#define WM8994_SYSCLK_OPCLK 5
+
 #define WM8994_FLL1 1
 #define WM8994_FLL2 2
 
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 13186fb..76b37ff 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1356,7 +1356,7 @@
 	ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
-		return ret;
+		goto err;
 	}
 
 	reg = snd_soc_read(codec, WM9081_SOFTWARE_RESET);
@@ -1369,7 +1369,7 @@
 	ret = wm9081_reset(codec);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to issue reset\n");
-		return ret;
+		goto err;
 	}
 
 	wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1388,18 +1388,19 @@
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		return ret;
+		goto err;
 	}
 
 	ret = snd_soc_register_dai(&wm9081_dai);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
-		snd_soc_unregister_codec(codec);
-		return ret;
+		goto err_codec;
 	}
 
 	return 0;
 
+err_codec:
+	snd_soc_unregister_codec(codec);
 err:
 	kfree(wm9081);
 	return ret;
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index 16f1a57..2cb8153 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -410,6 +410,8 @@
 				    WM8993_HPOUT1L_DLY |
 				    WM8993_HPOUT1R_DLY, 0);
 
+		snd_soc_write(codec, WM8993_DC_SERVO_0, 0);
+
 		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
 				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
 				    0);
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index adadcd3..9e8932a 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -26,6 +26,7 @@
 #include <mach/asp.h>
 
 #include "davinci-pcm.h"
+#include "davinci-i2s.h"
 
 
 /*
@@ -68,16 +69,21 @@
 #define DAVINCI_MCBSP_RCR_RDATDLY(v)	((v) << 16)
 #define DAVINCI_MCBSP_RCR_RFIG		(1 << 18)
 #define DAVINCI_MCBSP_RCR_RWDLEN2(v)	((v) << 21)
+#define DAVINCI_MCBSP_RCR_RFRLEN2(v)	((v) << 24)
+#define DAVINCI_MCBSP_RCR_RPHASE	BIT(31)
 
 #define DAVINCI_MCBSP_XCR_XWDLEN1(v)	((v) << 5)
 #define DAVINCI_MCBSP_XCR_XFRLEN1(v)	((v) << 8)
 #define DAVINCI_MCBSP_XCR_XDATDLY(v)	((v) << 16)
 #define DAVINCI_MCBSP_XCR_XFIG		(1 << 18)
 #define DAVINCI_MCBSP_XCR_XWDLEN2(v)	((v) << 21)
+#define DAVINCI_MCBSP_XCR_XFRLEN2(v)	((v) << 24)
+#define DAVINCI_MCBSP_XCR_XPHASE	BIT(31)
 
 #define DAVINCI_MCBSP_SRGR_FWID(v)	((v) << 8)
 #define DAVINCI_MCBSP_SRGR_FPER(v)	((v) << 16)
 #define DAVINCI_MCBSP_SRGR_FSGM		(1 << 28)
+#define DAVINCI_MCBSP_SRGR_CLKSM	BIT(29)
 
 #define DAVINCI_MCBSP_PCR_CLKRP		(1 << 0)
 #define DAVINCI_MCBSP_PCR_CLKXP		(1 << 1)
@@ -116,6 +122,7 @@
 };
 
 struct davinci_mcbsp_dev {
+	struct device *dev;
 	struct davinci_pcm_dma_params	dma_params[2];
 	void __iomem			*base;
 #define MOD_DSP_A	0
@@ -144,6 +151,11 @@
 	 * won't end up being swapped because of the underrun.
 	 */
 	unsigned enable_channel_combine:1;
+
+	unsigned int fmt;
+	int clk_div;
+	int clk_input_pin;
+	bool i2s_accurate_sck;
 };
 
 static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -254,10 +266,12 @@
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	unsigned int pcr;
 	unsigned int srgr;
+	/* Attention srgr is updated by hw_params! */
 	srgr = DAVINCI_MCBSP_SRGR_FSGM |
 		DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
 		DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
 
+	dev->fmt = fmt;
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBS_CFS:
@@ -268,11 +282,26 @@
 			DAVINCI_MCBSP_PCR_CLKRM;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFS:
-		/* McBSP CLKR pin is the input for the Sample Rate Generator.
-		 * McBSP FSR and FSX are driven by the Sample Rate Generator. */
-		pcr = DAVINCI_MCBSP_PCR_SCLKME |
-			DAVINCI_MCBSP_PCR_FSXM |
-			DAVINCI_MCBSP_PCR_FSRM;
+		pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
+		/*
+		 * Selection of the clock input pin that is the
+		 * input for the Sample Rate Generator.
+		 * McBSP FSR and FSX are driven by the Sample Rate
+		 * Generator.
+		 */
+		switch (dev->clk_input_pin) {
+		case MCBSP_CLKS:
+			pcr |= DAVINCI_MCBSP_PCR_CLKXM |
+				DAVINCI_MCBSP_PCR_CLKRM;
+			break;
+		case MCBSP_CLKR:
+			pcr |= DAVINCI_MCBSP_PCR_SCLKME;
+			break;
+		default:
+			dev_err(dev->dev, "bad clk_input_pin\n");
+			return -EINVAL;
+		}
+
 		break;
 	case SND_SOC_DAIFMT_CBM_CFM:
 		/* codec is master */
@@ -372,6 +401,18 @@
 	return 0;
 }
 
+static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
+				int div_id, int div)
+{
+	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+
+	if (div_id != DAVINCI_MCBSP_CLKGDV)
+		return -ENODEV;
+
+	dev->clk_div = div;
+	return 0;
+}
+
 static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
 				 struct snd_pcm_hw_params *params,
 				 struct snd_soc_dai *dai)
@@ -380,8 +421,8 @@
 	struct davinci_pcm_dma_params *dma_params =
 					&dev->dma_params[substream->stream];
 	struct snd_interval *i = NULL;
-	int mcbsp_word_length;
-	unsigned int rcr, xcr, srgr;
+	int mcbsp_word_length, master;
+	unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
 	u32 spcr;
 	snd_pcm_format_t fmt;
 	unsigned element_cnt = 1;
@@ -396,12 +437,59 @@
 		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
 	}
 
-	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
-	srgr = DAVINCI_MCBSP_SRGR_FSGM;
-	srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
+	master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+	fmt = params_format(params);
+	mcbsp_word_length = asp_word_length[fmt];
 
-	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
-	srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+	switch (master) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		freq = clk_get_rate(dev->clk);
+		srgr = DAVINCI_MCBSP_SRGR_FSGM |
+		       DAVINCI_MCBSP_SRGR_CLKSM;
+		srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
+						8 - 1);
+		if (dev->i2s_accurate_sck) {
+			clk_div = 256;
+			do {
+				framesize = (freq / (--clk_div)) /
+				params->rate_num *
+					params->rate_den;
+			} while (((framesize < 33) || (framesize > 4095)) &&
+				 (clk_div));
+			clk_div--;
+			srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1);
+		} else {
+			/* symmetric waveforms */
+			clk_div = freq / (mcbsp_word_length * 16) /
+				  params->rate_num * params->rate_den;
+			srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
+							16 - 1);
+		}
+		clk_div &= 0xFF;
+		srgr |= clk_div;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		srgr = DAVINCI_MCBSP_SRGR_FSGM;
+		clk_div = dev->clk_div - 1;
+		srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
+		srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
+		clk_div &= 0xFF;
+		srgr |= clk_div;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* Clock and frame sync given from external sources */
+		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+		srgr = DAVINCI_MCBSP_SRGR_FSGM;
+		srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
+		pr_debug("%s - %d  FWID set: re-read srgr = %X\n",
+			__func__, __LINE__, snd_interval_value(i) - 1);
+
+		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
+		srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+		break;
+	default:
+		return -EINVAL;
+	}
 	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
 
 	rcr = DAVINCI_MCBSP_RCR_RFIG;
@@ -426,12 +514,41 @@
 			element_cnt = 1;
 			fmt = double_fmt[fmt];
 		}
+		switch (master) {
+		case SND_SOC_DAIFMT_CBS_CFS:
+		case SND_SOC_DAIFMT_CBS_CFM:
+			rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
+			xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
+			rcr |= DAVINCI_MCBSP_RCR_RPHASE;
+			xcr |= DAVINCI_MCBSP_XCR_XPHASE;
+			break;
+		case SND_SOC_DAIFMT_CBM_CFM:
+		case SND_SOC_DAIFMT_CBM_CFS:
+			rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
+			xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
+			break;
+		default:
+			return -EINVAL;
+		}
 	}
 	dma_params->acnt = dma_params->data_type = data_type[fmt];
 	dma_params->fifo_level = 0;
 	mcbsp_word_length = asp_word_length[fmt];
-	rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
-	xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
+
+	switch (master) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_CBS_CFM:
+		rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
+		xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_CBM_CFS:
+		rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+		xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
 		DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@@ -442,6 +559,10 @@
 		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
 	else
 		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+
+	pr_debug("%s - %d  srgr=%X\n", __func__, __LINE__, srgr);
+	pr_debug("%s - %d  xcr=%X\n", __func__, __LINE__, xcr);
+	pr_debug("%s - %d  rcr=%X\n", __func__, __LINE__, rcr);
 	return 0;
 }
 
@@ -500,6 +621,7 @@
 	.trigger	= davinci_i2s_trigger,
 	.hw_params	= davinci_i2s_hw_params,
 	.set_fmt	= davinci_i2s_set_dai_fmt,
+	.set_clkdiv	= davinci_i2s_dai_set_clkdiv,
 
 };
 
@@ -526,6 +648,8 @@
 	struct snd_platform_data *pdata = pdev->dev.platform_data;
 	struct davinci_mcbsp_dev *dev;
 	struct resource *mem, *ioarea, *res;
+	enum dma_event_q asp_chan_q = EVENTQ_0;
+	enum dma_event_q ram_chan_q = EVENTQ_1;
 	int ret;
 
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -552,7 +676,17 @@
 			pdata->sram_size_playback;
 		dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
 			pdata->sram_size_capture;
+		dev->clk_input_pin = pdata->clk_input_pin;
+		dev->i2s_accurate_sck = pdata->i2s_accurate_sck;
+		asp_chan_q = pdata->asp_chan_q;
+		ram_chan_q = pdata->ram_chan_q;
 	}
+
+	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q	= asp_chan_q;
+	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q	= ram_chan_q;
+	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q	= asp_chan_q;
+	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q	= ram_chan_q;
+
 	dev->clk = clk_get(&pdev->dev, NULL);
 	if (IS_ERR(dev->clk)) {
 		ret = -ENODEV;
@@ -584,6 +718,7 @@
 		goto err_free_mem;
 	}
 	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
+	dev->dev = &pdev->dev;
 
 	davinci_i2s_dai.private_data = dev;
 	davinci_i2s_dai.capture.dma_data = dev->dma_params;
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
index 241648c..0b1e77b 100644
--- a/sound/soc/davinci/davinci-i2s.h
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -12,6 +12,11 @@
 #ifndef _DAVINCI_I2S_H
 #define _DAVINCI_I2S_H
 
+/* McBSP dividers */
+enum davinci_mcbsp_div {
+	DAVINCI_MCBSP_CLKGDV,              /* Sample rate generator divider */
+};
+
 extern struct snd_soc_dai davinci_i2s_dai;
 
 #endif
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index d395509..b247208 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -890,7 +890,8 @@
 	dev->rxnumevt = pdata->rxnumevt;
 
 	dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
-	dma_data->eventq_no = pdata->eventq_no;
+	dma_data->asp_chan_q = pdata->asp_chan_q;
+	dma_data->ram_chan_q = pdata->ram_chan_q;
 	dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
 							io_v2p(dev->base));
 
@@ -904,7 +905,8 @@
 	dma_data->channel = res->start;
 
 	dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
-	dma_data->eventq_no = pdata->eventq_no;
+	dma_data->asp_chan_q = pdata->asp_chan_q;
+	dma_data->ram_chan_q = pdata->ram_chan_q;
 	dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
 							io_v2p(dev->base));
 
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 2dc406f..a712411 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -381,7 +381,7 @@
 	/* Request ram master channel */
 	link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
 				  davinci_pcm_dma_irq, substream,
-				  EVENTQ_1);
+				  prtd->params->ram_chan_q);
 	if (link < 0)
 		goto exit1;
 
@@ -477,7 +477,8 @@
 
 	/* Request asp master DMA channel */
 	link = prtd->asp_channel = edma_alloc_channel(params->channel,
-			davinci_pcm_dma_irq, substream, EVENTQ_0);
+			davinci_pcm_dma_irq, substream,
+			prtd->params->asp_chan_q);
 	if (link < 0)
 		goto exit1;
 
@@ -800,7 +801,7 @@
 		dma_free_writecombine(pcm->card->dev, buf->bytes,
 				      buf->area, buf->addr);
 		buf->area = NULL;
-		iram_dma = (struct snd_dma_buffer *)buf->private_data;
+		iram_dma = buf->private_data;
 		if (iram_dma) {
 			sram_free(iram_dma->area, iram_dma->bytes);
 			kfree(iram_dma);
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 0764944..b799a02 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -21,7 +21,8 @@
 	unsigned short acnt;
 	dma_addr_t dma_addr;		/* device physical address for DMA */
 	unsigned sram_size;
-	enum dma_event_q eventq_no;	/* event queue number */
+	enum dma_event_q asp_chan_q;	/* event queue number for ASP channel */
+	enum dma_event_q ram_chan_q;	/* event queue number for RAM channel */
 	unsigned char data_type;	/* xfer data type */
 	unsigned char convert_mono_stereo;
 	unsigned int fifo_level;
diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c
index 9aa980d..4867853 100644
--- a/sound/soc/davinci/davinci-vcif.c
+++ b/sound/soc/davinci/davinci-vcif.c
@@ -203,7 +203,7 @@
 	int ret;
 
 	davinci_vcif_dev = kzalloc(sizeof(struct davinci_vcif_dev), GFP_KERNEL);
-	if (!davinci_vc) {
+	if (!davinci_vcif_dev) {
 		dev_dbg(&pdev->dev,
 			"could not allocate memory for private data\n");
 		return -ENOMEM;
diff --git a/sound/soc/ep93xx/Kconfig b/sound/soc/ep93xx/Kconfig
new file mode 100644
index 0000000..f617f56
--- /dev/null
+++ b/sound/soc/ep93xx/Kconfig
@@ -0,0 +1,18 @@
+config SND_EP93XX_SOC
+	tristate "SoC Audio support for the Cirrus Logic EP93xx series"
+	depends on ARCH_EP93XX && SND_SOC
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the EP93xx I2S interface.
+
+config SND_EP93XX_SOC_I2S
+	tristate
+
+config SND_EP93XX_SOC_SNAPPERCL15
+        tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
+        depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
+        select SND_EP93XX_SOC_I2S
+        select SND_SOC_TLV320AIC23
+        help
+          Say Y or M here if you want to add support for I2S audio on the
+          Bluewater Systems Snapper CL15 module.
diff --git a/sound/soc/ep93xx/Makefile b/sound/soc/ep93xx/Makefile
new file mode 100644
index 0000000..272e60f
--- /dev/null
+++ b/sound/soc/ep93xx/Makefile
@@ -0,0 +1,11 @@
+# EP93xx Platform Support
+snd-soc-ep93xx-objs				:= ep93xx-pcm.o
+snd-soc-ep93xx-i2s-objs	 			:= ep93xx-i2s.o
+
+obj-$(CONFIG_SND_EP93XX_SOC)			+= snd-soc-ep93xx.o
+obj-$(CONFIG_SND_EP93XX_SOC_I2S)		+= snd-soc-ep93xx-i2s.o
+
+# EP93XX Machine Support
+snd-soc-snappercl15-objs			:= snappercl15.o
+
+obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15)	+= snd-soc-snappercl15.o
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c
new file mode 100644
index 0000000..00b9466
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-i2s.c
@@ -0,0 +1,487 @@
+/*
+ * linux/sound/soc/ep93xx-i2s.c
+ * EP93xx I2S driver
+ *
+ * Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.com>
+ *
+ * Based on the original driver by:
+ *   Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
+ *   Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+#include <mach/ep93xx-regs.h>
+#include <mach/dma.h>
+
+#include "ep93xx-pcm.h"
+#include "ep93xx-i2s.h"
+
+#define EP93XX_I2S_TXCLKCFG		0x00
+#define EP93XX_I2S_RXCLKCFG		0x04
+#define EP93XX_I2S_GLCTRL		0x0C
+
+#define EP93XX_I2S_TXLINCTRLDATA	0x28
+#define EP93XX_I2S_TXCTRL		0x2C
+#define EP93XX_I2S_TXWRDLEN		0x30
+#define EP93XX_I2S_TX0EN		0x34
+
+#define EP93XX_I2S_RXLINCTRLDATA	0x58
+#define EP93XX_I2S_RXCTRL		0x5C
+#define EP93XX_I2S_RXWRDLEN		0x60
+#define EP93XX_I2S_RX0EN		0x64
+
+#define EP93XX_I2S_WRDLEN_16		(0 << 0)
+#define EP93XX_I2S_WRDLEN_24		(1 << 0)
+#define EP93XX_I2S_WRDLEN_32		(2 << 0)
+
+#define EP93XX_I2S_LINCTRLDATA_R_JUST	(1 << 2) /* Right justify */
+
+#define EP93XX_I2S_CLKCFG_LRS		(1 << 0) /* lrclk polarity */
+#define EP93XX_I2S_CLKCFG_CKP		(1 << 1) /* Bit clock polarity */
+#define EP93XX_I2S_CLKCFG_REL		(1 << 2) /* First bit transition */
+#define EP93XX_I2S_CLKCFG_MASTER	(1 << 3) /* Master mode */
+#define EP93XX_I2S_CLKCFG_NBCG		(1 << 4) /* Not bit clock gating */
+
+struct ep93xx_i2s_info {
+	struct clk			*mclk;
+	struct clk			*sclk;
+	struct clk			*lrclk;
+	struct ep93xx_pcm_dma_params	*dma_params;
+	struct resource			*mem;
+	void __iomem			*regs;
+};
+
+struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
+	[SNDRV_PCM_STREAM_PLAYBACK] = {
+		.name		= "i2s-pcm-out",
+		.dma_port	= EP93XX_DMA_M2P_PORT_I2S1,
+	},
+	[SNDRV_PCM_STREAM_CAPTURE] = {
+		.name		= "i2s-pcm-in",
+		.dma_port	= EP93XX_DMA_M2P_PORT_I2S1,
+	},
+};
+
+static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
+					unsigned reg, unsigned val)
+{
+	__raw_writel(val, info->regs + reg);
+}
+
+static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
+					   unsigned reg)
+{
+	return __raw_readl(info->regs + reg);
+}
+
+static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
+{
+	unsigned base_reg;
+	int i;
+
+	if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
+	    (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
+		/* Enable clocks */
+		clk_enable(info->mclk);
+		clk_enable(info->sclk);
+		clk_enable(info->lrclk);
+
+		/* Enable i2s */
+		ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
+	}
+
+	/* Enable fifos */
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		base_reg = EP93XX_I2S_TX0EN;
+	else
+		base_reg = EP93XX_I2S_RX0EN;
+	for (i = 0; i < 3; i++)
+		ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
+}
+
+static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
+{
+	unsigned base_reg;
+	int i;
+
+	/* Disable fifos */
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		base_reg = EP93XX_I2S_TX0EN;
+	else
+		base_reg = EP93XX_I2S_RX0EN;
+	for (i = 0; i < 3; i++)
+		ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
+
+	if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
+	    (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
+		/* Disable i2s */
+		ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
+
+		/* Disable clocks */
+		clk_disable(info->lrclk);
+		clk_disable(info->sclk);
+		clk_disable(info->mclk);
+	}
+}
+
+static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct ep93xx_i2s_info *info = rtd->dai->cpu_dai->private_data;
+
+	snd_soc_dai_set_dma_data(cpu_dai, substream,
+				 &info->dma_params[substream->stream]);
+	return 0;
+}
+
+static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ep93xx_i2s_info *info = rtd->dai->cpu_dai->private_data;
+
+	ep93xx_i2s_disable(info, substream->stream);
+}
+
+static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+				  unsigned int fmt)
+{
+	struct ep93xx_i2s_info *info = cpu_dai->private_data;
+	unsigned int clk_cfg, lin_ctrl;
+
+	clk_cfg  = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
+	lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		clk_cfg |= EP93XX_I2S_CLKCFG_REL;
+		lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
+		break;
+
+	case SND_SOC_DAIFMT_LEFT_J:
+		clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+		lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
+		break;
+
+	case SND_SOC_DAIFMT_RIGHT_J:
+		clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+		lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* CPU is master */
+		clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* Codec is master */
+		clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		/* Negative bit clock, lrclk low on left word */
+		clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
+		break;
+
+	case SND_SOC_DAIFMT_NB_IF:
+		/* Negative bit clock, lrclk low on right word */
+		clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
+		clk_cfg |= EP93XX_I2S_CLKCFG_REL;
+		break;
+
+	case SND_SOC_DAIFMT_IB_NF:
+		/* Positive bit clock, lrclk low on left word */
+		clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
+		clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
+		break;
+
+	case SND_SOC_DAIFMT_IB_IF:
+		/* Positive bit clock, lrclk low on right word */
+		clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
+		break;
+	}
+
+	/* Write new register values */
+	ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
+	ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
+	ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
+	ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
+	return 0;
+}
+
+static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct ep93xx_i2s_info *info = cpu_dai->private_data;
+	unsigned word_len, div, sdiv, lrdiv;
+	int found = 0, err;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		word_len = EP93XX_I2S_WRDLEN_16;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		word_len = EP93XX_I2S_WRDLEN_24;
+		break;
+
+	case SNDRV_PCM_FORMAT_S32_LE:
+		word_len = EP93XX_I2S_WRDLEN_32;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len);
+	else
+		ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len);
+
+	/*
+	 * Calculate the sdiv (bit clock) and lrdiv (left/right clock) values.
+	 * If the lrclk is pulse length is larger than the word size, then the
+	 * bit clock will be gated for the unused bits.
+	 */
+	div = (clk_get_rate(info->mclk) / params_rate(params)) *
+		params_channels(params);
+	for (sdiv = 2; sdiv <= 4; sdiv += 2)
+		for (lrdiv = 32; lrdiv <= 128; lrdiv <<= 1)
+			if (sdiv * lrdiv == div) {
+				found = 1;
+				goto out;
+			}
+out:
+	if (!found)
+		return -EINVAL;
+
+	err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
+	if (err)
+		return err;
+
+	err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv);
+	if (err)
+		return err;
+
+	ep93xx_i2s_enable(info, substream->stream);
+	return 0;
+}
+
+static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
+				 unsigned int freq, int dir)
+{
+	struct ep93xx_i2s_info *info = cpu_dai->private_data;
+
+	if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
+		return -EINVAL;
+
+	return clk_set_rate(info->mclk, freq);
+}
+
+#ifdef CONFIG_PM
+static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
+{
+	struct ep93xx_i2s_info *info = dai->private_data;
+
+	if (!dai->active)
+		return;
+
+	ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
+	ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
+{
+	struct ep93xx_i2s_info *info = dai->private_data;
+
+	if (!dai->active)
+		return;
+
+	ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
+	ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
+}
+#else
+#define ep93xx_i2s_suspend	NULL
+#define ep93xx_i2s_resume	NULL
+#endif
+
+static struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
+	.startup	= ep93xx_i2s_startup,
+	.shutdown	= ep93xx_i2s_shutdown,
+	.hw_params	= ep93xx_i2s_hw_params,
+	.set_sysclk	= ep93xx_i2s_set_sysclk,
+	.set_fmt	= ep93xx_i2s_set_dai_fmt,
+};
+
+#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+			    SNDRV_PCM_FMTBIT_S24_LE | \
+			    SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai ep93xx_i2s_dai = {
+	.name		= "ep93xx-i2s",
+	.id		= 0,
+	.symmetric_rates= 1,
+	.suspend	= ep93xx_i2s_suspend,
+	.resume		= ep93xx_i2s_resume,
+	.playback	= {
+		.channels_min	= 2,
+		.channels_max	= 2,
+		.rates		= SNDRV_PCM_RATE_8000_48000,
+		.formats	= EP93XX_I2S_FORMATS,
+	},
+	.capture	= {
+		 .channels_min	= 2,
+		 .channels_max	= 2,
+		 .rates		= SNDRV_PCM_RATE_8000_48000,
+		 .formats	= EP93XX_I2S_FORMATS,
+	},
+	.ops		= &ep93xx_i2s_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ep93xx_i2s_dai);
+
+static int ep93xx_i2s_probe(struct platform_device *pdev)
+{
+	struct ep93xx_i2s_info *info;
+	struct resource *res;
+	int err;
+
+	info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL);
+	if (!info) {
+		err = -ENOMEM;
+		goto fail;
+	}
+
+	ep93xx_i2s_dai.dev = &pdev->dev;
+	ep93xx_i2s_dai.private_data = info;
+	info->dma_params = ep93xx_i2s_dma_params;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		err = -ENODEV;
+		goto fail;
+	}
+
+	info->mem = request_mem_region(res->start, resource_size(res),
+				       pdev->name);
+	if (!info->mem) {
+		err = -EBUSY;
+		goto fail;
+	}
+
+	info->regs = ioremap(info->mem->start, resource_size(info->mem));
+	if (!info->regs) {
+		err = -ENXIO;
+		goto fail_release_mem;
+	}
+
+	info->mclk = clk_get(&pdev->dev, "mclk");
+	if (IS_ERR(info->mclk)) {
+		err = PTR_ERR(info->mclk);
+		goto fail_unmap_mem;
+	}
+
+	info->sclk = clk_get(&pdev->dev, "sclk");
+	if (IS_ERR(info->sclk)) {
+		err = PTR_ERR(info->sclk);
+		goto fail_put_mclk;
+	}
+
+	info->lrclk = clk_get(&pdev->dev, "lrclk");
+	if (IS_ERR(info->lrclk)) {
+		err = PTR_ERR(info->lrclk);
+		goto fail_put_sclk;
+	}
+
+	err = snd_soc_register_dai(&ep93xx_i2s_dai);
+	if (err)
+		goto fail_put_lrclk;
+
+	return 0;
+
+fail_put_lrclk:
+	clk_put(info->lrclk);
+fail_put_sclk:
+	clk_put(info->sclk);
+fail_put_mclk:
+	clk_put(info->mclk);
+fail_unmap_mem:
+	iounmap(info->regs);
+fail_release_mem:
+	release_mem_region(info->mem->start, resource_size(info->mem));
+	kfree(info);
+fail:
+	return err;
+}
+
+static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
+{
+	struct ep93xx_i2s_info *info = ep93xx_i2s_dai.private_data;
+
+	snd_soc_unregister_dai(&ep93xx_i2s_dai);
+	clk_put(info->lrclk);
+	clk_put(info->sclk);
+	clk_put(info->mclk);
+	iounmap(info->regs);
+	release_mem_region(info->mem->start, resource_size(info->mem));
+	kfree(info);
+	return 0;
+}
+
+static struct platform_driver ep93xx_i2s_driver = {
+	.probe	= ep93xx_i2s_probe,
+	.remove	= __devexit_p(ep93xx_i2s_remove),
+	.driver	= {
+		.name	= "ep93xx-i2s",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init ep93xx_i2s_init(void)
+{
+	return platform_driver_register(&ep93xx_i2s_driver);
+}
+
+static void __exit ep93xx_i2s_exit(void)
+{
+	platform_driver_unregister(&ep93xx_i2s_driver);
+}
+
+module_init(ep93xx_i2s_init);
+module_exit(ep93xx_i2s_exit);
+
+MODULE_ALIAS("platform:ep93xx-i2s");
+MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
+MODULE_DESCRIPTION("EP93XX I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ep93xx/ep93xx-i2s.h b/sound/soc/ep93xx/ep93xx-i2s.h
new file mode 100644
index 0000000..3bd4ebf
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-i2s.h
@@ -0,0 +1,18 @@
+/*
+ * linux/sound/soc/ep93xx-i2s.h
+ * EP93xx I2S driver
+ *
+ * Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.com>
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _EP93XX_SND_SOC_I2S_H
+#define _EP93XX_SND_SOC_I2S_H
+
+extern struct snd_soc_dai ep93xx_i2s_dai;
+
+#endif /* _EP93XX_SND_SOC_I2S_H */
diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c
new file mode 100644
index 0000000..4ba9384
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-pcm.c
@@ -0,0 +1,319 @@
+/*
+ * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ *
+ * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
+ *   Copyright (c) 2008 Ryan Mallon <ryan@bluewatersys.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/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/dma.h>
+#include <mach/hardware.h>
+#include <mach/ep93xx-regs.h>
+
+#include "ep93xx-pcm.h"
+
+static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
+	.info			= (SNDRV_PCM_INFO_MMAP		|
+				   SNDRV_PCM_INFO_MMAP_VALID	|
+				   SNDRV_PCM_INFO_INTERLEAVED	|
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER),
+				   
+	.rates			= SNDRV_PCM_RATE_8000_48000,
+	.rate_min		= SNDRV_PCM_RATE_8000,
+	.rate_max		= SNDRV_PCM_RATE_48000,
+	
+	.formats		= (SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE),
+	
+	.buffer_bytes_max	= 131072,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 32768,
+	.periods_min		= 1,
+	.periods_max		= 32,
+	.fifo_size		= 32,
+};
+
+struct ep93xx_runtime_data
+{
+	struct ep93xx_dma_m2p_client	cl;
+	struct ep93xx_pcm_dma_params	*params;
+	int				pointer_bytes;
+	struct tasklet_struct		period_tasklet;
+	int				periods;
+	struct ep93xx_dma_buffer	buf[32];
+};
+
+static void ep93xx_pcm_period_elapsed(unsigned long data)
+{
+	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
+	snd_pcm_period_elapsed(substream);
+}
+
+static void ep93xx_pcm_buffer_started(void *cookie,
+				      struct ep93xx_dma_buffer *buf)
+{
+}
+
+static void ep93xx_pcm_buffer_finished(void *cookie, 
+				       struct ep93xx_dma_buffer *buf, 
+				       int bytes, int error)
+{
+	struct snd_pcm_substream *substream = cookie;
+	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
+
+	if (buf == rtd->buf + rtd->periods - 1)
+		rtd->pointer_bytes = 0;
+	else
+		rtd->pointer_bytes += buf->size;
+
+	if (!error) {
+		ep93xx_dma_m2p_submit_recursive(&rtd->cl, buf);
+		tasklet_schedule(&rtd->period_tasklet);
+	} else {
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+	}
+}
+
+static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = soc_rtd->dai->cpu_dai;
+	struct ep93xx_pcm_dma_params *dma_params;
+	struct ep93xx_runtime_data *rtd;    
+	int ret;
+
+	dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
+	snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
+
+	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+	if (!rtd) 
+		return -ENOMEM;
+
+	memset(&rtd->period_tasklet, 0, sizeof(rtd->period_tasklet));
+	rtd->period_tasklet.func = ep93xx_pcm_period_elapsed;
+	rtd->period_tasklet.data = (unsigned long)substream;
+
+	rtd->cl.name = dma_params->name;
+	rtd->cl.flags = dma_params->dma_port | EP93XX_DMA_M2P_IGNORE_ERROR |
+		((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		 EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX);
+	rtd->cl.cookie = substream;
+	rtd->cl.buffer_started = ep93xx_pcm_buffer_started;
+	rtd->cl.buffer_finished = ep93xx_pcm_buffer_finished;
+	ret = ep93xx_dma_m2p_client_register(&rtd->cl);
+	if (ret < 0) {
+		kfree(rtd);
+		return ret;
+	}
+	
+	substream->runtime->private_data = rtd;
+	return 0;
+}
+
+static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
+
+	ep93xx_dma_m2p_client_unregister(&rtd->cl);
+	kfree(rtd);
+	return 0;
+}
+
+static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ep93xx_runtime_data *rtd = runtime->private_data;
+	size_t totsize = params_buffer_bytes(params);
+	size_t period = params_period_bytes(params);
+	int i;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = totsize;
+
+	rtd->periods = (totsize + period - 1) / period;
+	for (i = 0; i < rtd->periods; i++) {
+		rtd->buf[i].bus_addr = runtime->dma_addr + (i * period);
+		rtd->buf[i].size = period;
+		if ((i + 1) * period > totsize)
+			rtd->buf[i].size = totsize - (i * period);
+	}
+
+	return 0;
+}
+
+static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
+	int ret;
+	int i;
+
+	ret = 0;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		rtd->pointer_bytes = 0;
+		for (i = 0; i < rtd->periods; i++)
+			ep93xx_dma_m2p_submit(&rtd->cl, rtd->buf + i);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ep93xx_dma_m2p_flush(&rtd->cl);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t ep93xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
+
+	/* FIXME: implement this with sub-period granularity */
+	return bytes_to_frames(runtime, rtd->pointer_bytes);
+}
+
+static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
+			   struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops ep93xx_pcm_ops = {
+	.open		= ep93xx_pcm_open,
+	.close		= ep93xx_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= ep93xx_pcm_hw_params,
+	.hw_free	= ep93xx_pcm_hw_free,
+	.trigger	= ep93xx_pcm_trigger,
+	.pointer	= ep93xx_pcm_pointer,
+	.mmap		= ep93xx_pcm_mmap,
+};
+
+static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+					   &buf->addr, GFP_KERNEL);
+	buf->bytes = size;
+
+	return (buf->area == NULL) ? -ENOMEM : 0;
+}
+
+static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {		
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+
+		dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
+				      buf->addr);
+		buf->area = NULL;
+	}
+}
+
+static u64 ep93xx_pcm_dmamask = 0xffffffff;
+
+static int ep93xx_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 = &ep93xx_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (dai->playback.channels_min) {
+		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
+					SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			return ret;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
+					SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+struct snd_soc_platform ep93xx_soc_platform = {
+	.name		= "ep93xx-audio",
+	.pcm_ops	= &ep93xx_pcm_ops,
+	.pcm_new	= &ep93xx_pcm_new,
+	.pcm_free	= &ep93xx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(ep93xx_soc_platform);
+
+static int __init ep93xx_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&ep93xx_soc_platform);
+}
+
+static void __exit ep93xx_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&ep93xx_soc_platform);
+}
+
+module_init(ep93xx_soc_platform_init);
+module_exit(ep93xx_soc_platform_exit);
+
+MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
+MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ep93xx/ep93xx-pcm.h b/sound/soc/ep93xx/ep93xx-pcm.h
new file mode 100644
index 0000000..4ffdd3f
--- /dev/null
+++ b/sound/soc/ep93xx/ep93xx-pcm.h
@@ -0,0 +1,22 @@
+/*
+ * sound/soc/ep93xx/ep93xx-pcm.h - EP93xx ALSA PCM interface
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ *
+ * 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 _EP93XX_SND_SOC_PCM_H
+#define _EP93XX_SND_SOC_PCM_H
+
+struct ep93xx_pcm_dma_params {
+	char	*name;
+	int	dma_port;
+};
+
+extern struct snd_soc_platform ep93xx_soc_platform;
+
+#endif /* _EP93XX_SND_SOC_PCM_H */
diff --git a/sound/soc/ep93xx/snappercl15.c b/sound/soc/ep93xx/snappercl15.c
new file mode 100644
index 0000000..64955340
--- /dev/null
+++ b/sound/soc/ep93xx/snappercl15.c
@@ -0,0 +1,150 @@
+/*
+ * snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module
+ *
+ * Copyright (C) 2008 Bluewater Systems Ltd
+ * Author: Ryan Mallon <ryan@bluewatersys.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/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "../codecs/tlv320aic23.h"
+#include "ep93xx-pcm.h"
+#include "ep93xx-i2s.h"
+
+#define CODEC_CLOCK 5644800
+
+static int snappercl15_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;
+	int err;
+
+	err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+
+	err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | 
+				  SND_SOC_DAIFMT_NB_IF |		  
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (err)
+		return err;
+
+	err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, 
+				     SND_SOC_CLOCK_IN);
+	if (err)
+		return err;
+
+	err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK, 
+				     SND_SOC_CLOCK_OUT);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static struct snd_soc_ops snappercl15_ops = {
+	.hw_params	= snappercl15_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_LINE("Line In", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"Headphone Jack", NULL, "LHPOUT"},
+	{"Headphone Jack", NULL, "RHPOUT"},
+
+	{"LLINEIN", NULL, "Line In"},
+	{"RLINEIN", NULL, "Line In"},
+
+	{"MICIN", NULL, "Mic Jack"},
+};
+
+static int snappercl15_tlv320aic23_init(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+				  ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	return 0;
+}
+
+static struct snd_soc_dai_link snappercl15_dai = {
+	.name		= "tlv320aic23",
+	.stream_name	= "AIC23",
+	.cpu_dai	= &ep93xx_i2s_dai,
+	.codec_dai	= &tlv320aic23_dai,
+	.init		= snappercl15_tlv320aic23_init,
+	.ops		= &snappercl15_ops,
+};
+
+static struct snd_soc_card snd_soc_snappercl15 = {
+	.name		= "Snapper CL15",
+	.platform	= &ep93xx_soc_platform,
+	.dai_link	= &snappercl15_dai,
+	.num_links	= 1,
+};
+
+static struct snd_soc_device snappercl15_snd_devdata = {
+	.card		= &snd_soc_snappercl15,
+	.codec_dev	= &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *snappercl15_snd_device;
+
+static int __init snappercl15_init(void)
+{
+	int ret;
+
+	if (!machine_is_snapper_cl15())
+		return -ENODEV;
+
+	ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
+				 EP93XX_SYSCON_I2SCLKDIV_ORIDE |
+				 EP93XX_SYSCON_I2SCLKDIV_SPOL);
+	if (ret)
+		return ret;
+
+	snappercl15_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!snappercl15_snd_device)
+		return -ENOMEM;
+	
+	platform_set_drvdata(snappercl15_snd_device, &snappercl15_snd_devdata);
+	snappercl15_snd_devdata.dev = &snappercl15_snd_device->dev;
+	ret = platform_device_add(snappercl15_snd_device);
+	if (ret)
+		platform_device_put(snappercl15_snd_device);
+
+	return ret;
+}
+
+static void __exit snappercl15_exit(void)
+{
+	platform_device_unregister(snappercl15_snd_device);
+	ep93xx_i2s_release();
+}
+
+module_init(snappercl15_init);
+module_exit(snappercl15_exit);
+
+MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
+MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index 4f455bd..676841c 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -16,7 +16,6 @@
 
 #include <asm/mpc52xx_psc.h>
 
-#include "mpc5200_psc_i2s.h"
 #include "mpc5200_dma.h"
 
 /**
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.h b/sound/soc/fsl/mpc5200_psc_i2s.h
deleted file mode 100644
index ce55e07..0000000
--- a/sound/soc/fsl/mpc5200_psc_i2s.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Freescale MPC5200 PSC in I2S mode
- * ALSA SoC Digital Audio Interface (DAI) driver
- *
- */
-
-#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
-#define __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
-
-extern struct snd_soc_dai psc_i2s_dai[];
-
-#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ */
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index 252defe..52dac5e 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -1,4 +1,4 @@
-config SND_IMX_SOC
+menuconfig SND_IMX_SOC
 	tristate "SoC Audio for Freescale i.MX CPUs"
 	depends on ARCH_MXC
 	select SND_PCM
@@ -8,14 +8,12 @@
 	  Say Y or M if you want to add support for codecs attached to
 	  the i.MX SSI interface.
 
-config SND_MXC_SOC_SSI
-	tristate
+if SND_IMX_SOC
 
 config SND_MXC_SOC_WM1133_EV1
 	tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted"
-	depends on SND_IMX_SOC && MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
+	depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
 	select SND_SOC_WM8350
-	select SND_MXC_SOC_SSI
 	help
 	  Enable support for audio on the i.MX31ADS with the WM1133-EV1
 	  PMIC board with WM8835x fitted.
@@ -23,8 +21,17 @@
 config SND_SOC_PHYCORE_AC97
 	tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
 	depends on MACH_PCM043 || MACH_PCA100
-	select SND_MXC_SOC_SSI
 	select SND_SOC_WM9712
 	help
 	  Say Y if you want to add support for SoC audio on Phytec phyCORE
 	  and phyCARD boards in AC97 mode
+
+config SND_SOC_EUKREA_TLV320
+	tristate "Eukrea TLV320"
+	depends on MACH_EUKREA_MBIMX27_BASEBOARD || MACH_EUKREA_MBIMXSD_BASEBOARD
+	select SND_SOC_TLV320AIC23
+	help
+	  Enable I2S based access to the TLV320AIC23B codec attached
+	  to the SSI interface
+
+endif	# SND_IMX_SOC
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index 2d20363..7bc57ba 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -8,8 +8,10 @@
 obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o
 
 # i.MX Machine Support
+snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
 snd-soc-phycore-ac97-objs := phycore-ac97.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
 
+obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/imx/eukrea-tlv320.c
new file mode 100644
index 0000000..f15dfbd
--- /dev/null
+++ b/sound/soc/imx/eukrea-tlv320.c
@@ -0,0 +1,137 @@
+/*
+ * eukrea-tlv320.c  --  SoC audio for eukrea_cpuimxXX in I2S mode
+ *
+ * Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
+ *
+ * based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
+ * which is Copyright 2009 Simtec Electronics
+ * and on sound/soc/imx/phycore-ac97.c which is
+ * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * 
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+
+#include "../codecs/tlv320aic23.h"
+#include "imx-ssi.h"
+
+#define CODEC_CLOCK 12000000
+
+static int eukrea_tlv320_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;
+	int ret;
+
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret) {
+		pr_err("%s: failed set cpu dai format\n", __func__);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret) {
+		pr_err("%s: failed set codec dai format\n", __func__);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+				     CODEC_CLOCK, SND_SOC_CLOCK_OUT);
+	if (ret) {
+		pr_err("%s: failed setting codec sysclk\n", __func__);
+		return ret;
+	}
+	snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
+				SND_SOC_CLOCK_IN);
+	if (ret) {
+		pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops eukrea_tlv320_snd_ops = {
+	.hw_params	= eukrea_tlv320_hw_params,
+};
+
+static struct snd_soc_dai_link eukrea_tlv320_dai = {
+	.name		= "tlv320aic23",
+	.stream_name	= "TLV320AIC23",
+	.codec_dai	= &tlv320aic23_dai,
+	.ops		= &eukrea_tlv320_snd_ops,
+};
+
+static struct snd_soc_card eukrea_tlv320 = {
+	.name		= "cpuimx-audio",
+	.platform	= &imx_soc_platform,
+	.dai_link	= &eukrea_tlv320_dai,
+	.num_links	= 1,
+};
+
+static struct snd_soc_device eukrea_tlv320_snd_devdata = {
+	.card		= &eukrea_tlv320,
+	.codec_dev	= &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *eukrea_tlv320_snd_device;
+
+static int __init eukrea_tlv320_init(void)
+{
+	int ret;
+
+	if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd()
+		&& !machine_is_eukrea_cpuimx35sd())
+		/* return happy. We might run on a totally different machine */
+		return 0;
+
+	eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!eukrea_tlv320_snd_device)
+		return -ENOMEM;
+
+	eukrea_tlv320_dai.cpu_dai = &imx_ssi_pcm_dai[0];
+
+	platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320_snd_devdata);
+	eukrea_tlv320_snd_devdata.dev = &eukrea_tlv320_snd_device->dev;
+	ret = platform_device_add(eukrea_tlv320_snd_device);
+
+	if (ret) {
+		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+		platform_device_put(eukrea_tlv320_snd_device);
+	}
+
+	return ret;
+}
+
+static void __exit eukrea_tlv320_exit(void)
+{
+	platform_device_unregister(eukrea_tlv320_snd_device);
+}
+
+module_init(eukrea_tlv320_init);
+module_exit(eukrea_tlv320_exit);
+
+MODULE_AUTHOR("Eric Bénard <eric@eukrea.com>");
+MODULE_DESCRIPTION("CPUIMX ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c
index 05f19c9..0a595da 100644
--- a/sound/soc/imx/imx-pcm-dma-mx2.c
+++ b/sound/soc/imx/imx-pcm-dma-mx2.c
@@ -292,12 +292,16 @@
 	int ret;
 
 	iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+	if (iprtd == NULL)
+		return -ENOMEM;
 	runtime->private_data = iprtd;
 
 	ret = snd_pcm_hw_constraint_integer(substream->runtime,
 			SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
+	if (ret < 0) {
+		kfree(iprtd);
 		return ret;
+	}
 
 	snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
 	return 0;
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c
index 6b518e0..b2bf272 100644
--- a/sound/soc/imx/imx-pcm-fiq.c
+++ b/sound/soc/imx/imx-pcm-fiq.c
@@ -192,6 +192,8 @@
 	int ret;
 
 	iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+	if (iprtd == NULL)
+		return -ENOMEM;
 	runtime->private_data = iprtd;
 
 	iprtd->substream = substream;
@@ -202,8 +204,10 @@
 
 	ret = snd_pcm_hw_constraint_integer(substream->runtime,
 			SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
+	if (ret < 0) {
+		kfree(iprtd);
 		return ret;
+	}
 
 	snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
 	return 0;
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
index 4fd13d0..a11daa1 100644
--- a/sound/soc/imx/imx-ssi.c
+++ b/sound/soc/imx/imx-ssi.c
@@ -83,8 +83,6 @@
 /*
  * SSI DAI format configuration.
  * Should only be called when port is inactive (i.e. SSIEN = 0).
- * Note: We don't use the I2S modes but instead manually configure the
- * SSI for I2S because the I2S mode is only a register preset.
  */
 static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 {
@@ -99,6 +97,10 @@
 		/* data on rising edge of bclk, frame low 1clk before data */
 		strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
 		scr |= SSI_SCR_NET;
+		if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) {
+			scr &= ~SSI_I2S_MODE_MASK;
+			scr |= SSI_SCR_I2S_MODE_SLAVE;
+		}
 		break;
 	case SND_SOC_DAIFMT_LEFT_J:
 		/* data on rising edge of bclk, frame high with data */
@@ -143,6 +145,11 @@
 
 	strcr |= SSI_STCR_TFEN0;
 
+	if (ssi->flags & IMX_SSI_NET)
+		scr |= SSI_SCR_NET;
+	if (ssi->flags & IMX_SSI_SYN)
+		scr |= SSI_SCR_SYN;
+
 	writel(strcr, ssi->base + SSI_STCR);
 	writel(strcr, ssi->base + SSI_SRCR);
 	writel(scr, ssi->base + SSI_SCR);
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
new file mode 100644
index 0000000..5351cba
--- /dev/null
+++ b/sound/soc/jz4740/Kconfig
@@ -0,0 +1,23 @@
+config SND_JZ4740_SOC
+	tristate "SoC Audio for Ingenic JZ4740 SoC"
+	depends on MACH_JZ4740 && SND_SOC
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the JZ4740 I2S interface. You will also need to select the audio
+	  interfaces to support below.
+
+config SND_JZ4740_SOC_I2S
+	depends on SND_JZ4740_SOC
+	tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
+	help
+	  Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
+	  based boards.
+
+config SND_JZ4740_SOC_QI_LB60
+	tristate "SoC Audio support for Qi LB60"
+	depends on SND_JZ4740_SOC && JZ4740_QI_LB60
+	select SND_JZ4740_SOC_I2S
+    select SND_SOC_JZ4740_CODEC
+	help
+	  Say Y if you want to add support for ASoC audio on the Qi LB60 board
+	  a.k.a Qi Ben NanoNote.
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
new file mode 100644
index 0000000..be873c1
--- /dev/null
+++ b/sound/soc/jz4740/Makefile
@@ -0,0 +1,13 @@
+#
+# Jz4740 Platform Support
+#
+snd-soc-jz4740-objs := jz4740-pcm.o
+snd-soc-jz4740-i2s-objs := jz4740-i2s.o
+
+obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
+obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
+
+# Jz4740 Machine Support
+snd-soc-qi-lb60-objs := qi_lb60.o
+
+obj-$(CONFIG_SND_JZ4740_SOC_QI_LB60) += snd-soc-qi-lb60.o
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
new file mode 100644
index 0000000..eb518f0
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -0,0 +1,540 @@
+/*
+ *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  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/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <linux/dma-mapping.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 "jz4740-i2s.h"
+#include "jz4740-pcm.h"
+
+#define JZ_REG_AIC_CONF		0x00
+#define JZ_REG_AIC_CTRL		0x04
+#define JZ_REG_AIC_I2S_FMT	0x10
+#define JZ_REG_AIC_FIFO_STATUS	0x14
+#define JZ_REG_AIC_I2S_STATUS	0x1c
+#define JZ_REG_AIC_CLK_DIV	0x30
+#define JZ_REG_AIC_FIFO		0x34
+
+#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
+#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf <<  8)
+#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
+#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
+#define JZ_AIC_CONF_I2S BIT(4)
+#define JZ_AIC_CONF_RESET BIT(3)
+#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
+#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
+#define JZ_AIC_CONF_ENABLE BIT(0)
+
+#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
+#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
+
+#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
+#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
+#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
+#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
+#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
+#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
+#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
+#define JZ_AIC_CTRL_FLUSH		BIT(8)
+#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
+#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
+#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
+#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
+#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
+#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
+#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
+
+#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
+#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET  16
+
+#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
+#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
+#define JZ_AIC_I2S_FMT_MSB BIT(0)
+
+#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
+
+#define JZ_AIC_CLK_DIV_MASK 0xf
+
+struct jz4740_i2s {
+	struct resource *mem;
+	void __iomem *base;
+	dma_addr_t phys_base;
+
+	struct clk *clk_aic;
+	struct clk *clk_i2s;
+
+	struct jz4740_pcm_config pcm_config_playback;
+	struct jz4740_pcm_config pcm_config_capture;
+};
+
+static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
+	unsigned int reg)
+{
+	return readl(i2s->base + reg);
+}
+
+static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
+	unsigned int reg, uint32_t value)
+{
+	writel(value, i2s->base + reg);
+}
+
+static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
+{
+	return dai->private_data;
+}
+
+static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+	uint32_t conf, ctrl;
+
+	if (dai->active)
+		return 0;
+
+	ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+	ctrl |= JZ_AIC_CTRL_FLUSH;
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+	clk_enable(i2s->clk_i2s);
+
+	conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+	conf |= JZ_AIC_CONF_ENABLE;
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+	return 0;
+}
+
+static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+	uint32_t conf;
+
+	if (!dai->active)
+		return;
+
+	conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+	conf &= ~JZ_AIC_CONF_ENABLE;
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+	clk_disable(i2s->clk_i2s);
+}
+
+static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+	struct snd_soc_dai *dai)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+
+	uint32_t ctrl;
+	uint32_t mask;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
+	else
+		mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
+
+	ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ctrl |= mask;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ctrl &= ~mask;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+	return 0;
+}
+
+static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+
+	uint32_t format = 0;
+	uint32_t conf;
+
+	conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+
+	conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
+		format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_MSB:
+		format |= JZ_AIC_I2S_FMT_MSB;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+	jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
+
+	return 0;
+}
+
+static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+	enum jz4740_dma_width dma_width;
+	struct jz4740_pcm_config *pcm_config;
+	unsigned int sample_size;
+	uint32_t ctrl;
+
+	ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		sample_size = 0;
+		dma_width = JZ4740_DMA_WIDTH_8BIT;
+		break;
+	case SNDRV_PCM_FORMAT_S16:
+		sample_size = 1;
+		dma_width = JZ4740_DMA_WIDTH_16BIT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
+		ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
+		if (params_channels(params) == 1)
+			ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
+		else
+			ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
+
+		pcm_config = &i2s->pcm_config_playback;
+		pcm_config->dma_config.dst_width = dma_width;
+
+	} else {
+		ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
+		ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
+
+		pcm_config = &i2s->pcm_config_capture;
+		pcm_config->dma_config.src_width = dma_width;
+	}
+
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
+
+	snd_soc_dai_set_dma_data(dai, substream, pcm_config);
+
+	return 0;
+}
+
+static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+	unsigned int freq, int dir)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+	struct clk *parent;
+	int ret = 0;
+
+	switch (clk_id) {
+	case JZ4740_I2S_CLKSRC_EXT:
+		parent = clk_get(NULL, "ext");
+		clk_set_parent(i2s->clk_i2s, parent);
+		break;
+	case JZ4740_I2S_CLKSRC_PLL:
+		parent = clk_get(NULL, "pll half");
+		clk_set_parent(i2s->clk_i2s, parent);
+		ret = clk_set_rate(i2s->clk_i2s, freq);
+		break;
+	default:
+		return -EINVAL;
+	}
+	clk_put(parent);
+
+	return ret;
+}
+
+static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+	uint32_t conf;
+
+	if (dai->active) {
+		conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+		conf &= ~JZ_AIC_CONF_ENABLE;
+		jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+		clk_disable(i2s->clk_i2s);
+	}
+
+	clk_disable(i2s->clk_aic);
+
+	return 0;
+}
+
+static int jz4740_i2s_resume(struct snd_soc_dai *dai)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+	uint32_t conf;
+
+	clk_enable(i2s->clk_aic);
+
+	if (dai->active) {
+		clk_enable(i2s->clk_i2s);
+
+		conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
+		conf |= JZ_AIC_CONF_ENABLE;
+		jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+	}
+
+	return 0;
+}
+
+static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+	struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
+	uint32_t conf;
+
+	conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
+		(8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
+		JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
+		JZ_AIC_CONF_I2S |
+		JZ_AIC_CONF_INTERNAL_CODEC;
+
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
+	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
+	.startup = jz4740_i2s_startup,
+	.shutdown = jz4740_i2s_shutdown,
+	.trigger = jz4740_i2s_trigger,
+	.hw_params = jz4740_i2s_hw_params,
+	.set_fmt = jz4740_i2s_set_fmt,
+	.set_sysclk = jz4740_i2s_set_sysclk,
+};
+
+#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
+		SNDRV_PCM_FMTBIT_S16_LE)
+
+struct snd_soc_dai jz4740_i2s_dai = {
+	.name = "jz4740-i2s",
+	.probe = jz4740_i2s_probe,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = JZ4740_I2S_FMTS,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = JZ4740_I2S_FMTS,
+	},
+	.symmetric_rates = 1,
+	.ops = &jz4740_i2s_dai_ops,
+	.suspend = jz4740_i2s_suspend,
+	.resume = jz4740_i2s_resume,
+};
+EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
+
+static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
+{
+	struct jz4740_dma_config *dma_config;
+
+	/* Playback */
+	dma_config = &i2s->pcm_config_playback.dma_config;
+	dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
+	dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
+	dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
+	dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
+	dma_config->mode = JZ4740_DMA_MODE_SINGLE;
+	i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+
+	/* Capture */
+	dma_config = &i2s->pcm_config_capture.dma_config;
+	dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
+	dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
+	dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
+	dma_config->flags = JZ4740_DMA_DST_AUTOINC;
+	dma_config->mode = JZ4740_DMA_MODE_SINGLE;
+	i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+}
+
+static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
+{
+	struct jz4740_i2s *i2s;
+	int ret;
+
+	i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
+
+	if (!i2s)
+		return -ENOMEM;
+
+	i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!i2s->mem) {
+		ret = -ENOENT;
+		goto err_free;
+	}
+
+	i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
+				pdev->name);
+	if (!i2s->mem) {
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
+	if (!i2s->base) {
+		ret = -EBUSY;
+		goto err_release_mem_region;
+	}
+
+	i2s->phys_base = i2s->mem->start;
+
+	i2s->clk_aic = clk_get(&pdev->dev, "aic");
+	if (IS_ERR(i2s->clk_aic)) {
+		ret = PTR_ERR(i2s->clk_aic);
+		goto err_iounmap;
+	}
+
+	i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
+	if (IS_ERR(i2s->clk_i2s)) {
+		ret = PTR_ERR(i2s->clk_i2s);
+		goto err_clk_put_aic;
+	}
+
+	clk_enable(i2s->clk_aic);
+
+	jz4740_i2c_init_pcm_config(i2s);
+
+	jz4740_i2s_dai.private_data = i2s;
+	ret = snd_soc_register_dai(&jz4740_i2s_dai);
+
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register DAI\n");
+		goto err_clk_put_i2s;
+	}
+
+	platform_set_drvdata(pdev, i2s);
+
+	return 0;
+
+err_clk_put_i2s:
+	clk_disable(i2s->clk_aic);
+	clk_put(i2s->clk_i2s);
+err_clk_put_aic:
+	clk_put(i2s->clk_aic);
+err_iounmap:
+	iounmap(i2s->base);
+err_release_mem_region:
+	release_mem_region(i2s->mem->start, resource_size(i2s->mem));
+err_free:
+	kfree(i2s);
+
+	return ret;
+}
+
+static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
+{
+	struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_dai(&jz4740_i2s_dai);
+
+	clk_disable(i2s->clk_aic);
+	clk_put(i2s->clk_i2s);
+	clk_put(i2s->clk_aic);
+
+	iounmap(i2s->base);
+	release_mem_region(i2s->mem->start, resource_size(i2s->mem));
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(i2s);
+
+	return 0;
+}
+
+static struct platform_driver jz4740_i2s_driver = {
+	.probe = jz4740_i2s_dev_probe,
+	.remove = __devexit_p(jz4740_i2s_dev_remove),
+	.driver = {
+		.name = "jz4740-i2s",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init jz4740_i2s_init(void)
+{
+	return platform_driver_register(&jz4740_i2s_driver);
+}
+module_init(jz4740_i2s_init);
+
+static void __exit jz4740_i2s_exit(void)
+{
+	platform_driver_unregister(&jz4740_i2s_driver);
+}
+module_exit(jz4740_i2s_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
+MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jz4740-i2s");
diff --git a/sound/soc/jz4740/jz4740-i2s.h b/sound/soc/jz4740/jz4740-i2s.h
new file mode 100644
index 0000000..da22ed8
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-i2s.h
@@ -0,0 +1,18 @@
+/*
+ * 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 _JZ4740_I2S_H
+#define _JZ4740_I2S_H
+
+/* I2S clock source */
+#define JZ4740_I2S_CLKSRC_EXT 0
+#define JZ4740_I2S_CLKSRC_PLL 1
+
+#define JZ4740_I2S_BIT_CLK		0
+
+extern struct snd_soc_dai jz4740_i2s_dai;
+
+#endif
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
new file mode 100644
index 0000000..ee68d85
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-pcm.c
@@ -0,0 +1,373 @@
+/*
+ *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ *  This program is free software; you can redistribute	 it and/or modify it
+ *  under  the terms of	 the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the	License, or (at your
+ *  option) any later version.
+ *
+ *  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/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.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-jz4740/dma.h>
+#include "jz4740-pcm.h"
+
+struct jz4740_runtime_data {
+	unsigned long dma_period;
+	dma_addr_t dma_start;
+	dma_addr_t dma_pos;
+	dma_addr_t dma_end;
+
+	struct jz4740_dma_chan *dma;
+
+	dma_addr_t fifo_addr;
+};
+
+/* identify hardware playback capabilities */
+static const struct snd_pcm_hardware jz4740_pcm_hardware = {
+	.info = SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+
+	.rates			= SNDRV_PCM_RATE_8000_48000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.period_bytes_min	= 16,
+	.period_bytes_max	= 2 * PAGE_SIZE,
+	.periods_min		= 2,
+	.periods_max		= 128,
+	.buffer_bytes_max	= 128 * 2 * PAGE_SIZE,
+	.fifo_size		= 32,
+};
+
+static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
+	struct snd_pcm_substream *substream)
+{
+	unsigned long count;
+
+	if (prtd->dma_pos == prtd->dma_end)
+		prtd->dma_pos = prtd->dma_start;
+
+	if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
+		count = prtd->dma_end - prtd->dma_pos;
+	else
+		count = prtd->dma_period;
+
+	jz4740_dma_disable(prtd->dma);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
+		jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
+	} else {
+		jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
+		jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
+	}
+
+	jz4740_dma_set_transfer_count(prtd->dma, count);
+
+	prtd->dma_pos += count;
+
+	jz4740_dma_enable(prtd->dma);
+}
+
+static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
+	void *dev_id)
+{
+	struct snd_pcm_substream *substream = dev_id;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct jz4740_runtime_data *prtd = runtime->private_data;
+
+	snd_pcm_period_elapsed(substream);
+
+	jz4740_pcm_start_transfer(prtd, substream);
+}
+
+static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct jz4740_runtime_data *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct jz4740_pcm_config *config;
+
+	config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
+
+	if (!config)
+		return 0;
+
+	if (!prtd->dma) {
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->dma = jz4740_dma_request(substream, "PCM Capture");
+		else
+			prtd->dma = jz4740_dma_request(substream, "PCM Playback");
+	}
+
+	if (!prtd->dma)
+		return -EBUSY;
+
+	jz4740_dma_configure(prtd->dma, &config->dma_config);
+	prtd->fifo_addr = config->fifo_addr;
+
+	jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	prtd->dma_period = params_period_bytes(params);
+	prtd->dma_start = runtime->dma_addr;
+	prtd->dma_pos = prtd->dma_start;
+	prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
+
+	return 0;
+}
+
+static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct jz4740_runtime_data *prtd = substream->runtime->private_data;
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	if (prtd->dma) {
+		jz4740_dma_free(prtd->dma);
+		prtd->dma = NULL;
+	}
+
+	return 0;
+}
+
+static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct jz4740_runtime_data *prtd = substream->runtime->private_data;
+
+	if (!prtd->dma)
+		return -EBUSY;
+
+	prtd->dma_pos = prtd->dma_start;
+
+	return 0;
+}
+
+static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct jz4740_runtime_data *prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		jz4740_pcm_start_transfer(prtd, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		jz4740_dma_disable(prtd->dma);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct jz4740_runtime_data *prtd = runtime->private_data;
+	unsigned long byte_offset;
+	snd_pcm_uframes_t offset;
+	struct jz4740_dma_chan *dma = prtd->dma;
+
+	/* prtd->dma_pos points to the end of the current transfer. So by
+	 * subtracting prdt->dma_start we get the offset to the end of the
+	 * current period in bytes. By subtracting the residue of the transfer
+	 * we get the current offset in bytes. */
+	byte_offset = prtd->dma_pos - prtd->dma_start;
+	byte_offset -= jz4740_dma_get_residue(dma);
+
+	offset = bytes_to_frames(runtime, byte_offset);
+	if (offset >= runtime->buffer_size)
+		offset = 0;
+
+	return offset;
+}
+
+static int jz4740_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct jz4740_runtime_data *prtd;
+
+	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
+
+	runtime->private_data = prtd;
+
+	return 0;
+}
+
+static int jz4740_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct jz4740_runtime_data *prtd = runtime->private_data;
+
+	kfree(prtd);
+
+	return 0;
+}
+
+static int jz4740_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 jz4740_pcm_ops = {
+	.open		= jz4740_pcm_open,
+	.close		= jz4740_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= jz4740_pcm_hw_params,
+	.hw_free	= jz4740_pcm_hw_free,
+	.prepare	= jz4740_pcm_prepare,
+	.trigger	= jz4740_pcm_trigger,
+	.pointer	= jz4740_pcm_pointer,
+	.mmap		= jz4740_pcm_mmap,
+};
+
+static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = jz4740_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+
+	buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
+					  &buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+
+	buf->bytes = size;
+
+	return 0;
+}
+
+static void jz4740_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+
+		dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
+				buf->addr);
+		buf->area = NULL;
+	}
+}
+
+static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
+
+int jz4740_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 = &jz4740_pcm_dmamask;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	if (dai->playback.channels_min) {
+		ret = jz4740_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto err;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = jz4740_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto err;
+	}
+
+err:
+	return ret;
+}
+
+struct snd_soc_platform jz4740_soc_platform = {
+		.name		= "jz4740-pcm",
+		.pcm_ops	= &jz4740_pcm_ops,
+		.pcm_new	= jz4740_pcm_new,
+		.pcm_free	= jz4740_pcm_free,
+};
+EXPORT_SYMBOL_GPL(jz4740_soc_platform);
+
+static int __devinit jz4740_pcm_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_platform(&jz4740_soc_platform);
+}
+
+static int __devexit jz4740_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&jz4740_soc_platform);
+	return 0;
+}
+
+static struct platform_driver jz4740_pcm_driver = {
+	.probe = jz4740_pcm_probe,
+	.remove = __devexit_p(jz4740_pcm_remove),
+	.driver = {
+		.name = "jz4740-pcm",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init jz4740_soc_platform_init(void)
+{
+	return platform_driver_register(&jz4740_pcm_driver);
+}
+module_init(jz4740_soc_platform_init);
+
+static void __exit jz4740_soc_platform_exit(void)
+{
+	return platform_driver_unregister(&jz4740_pcm_driver);
+}
+module_exit(jz4740_soc_platform_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
new file mode 100644
index 0000000..e3f221e
--- /dev/null
+++ b/sound/soc/jz4740/jz4740-pcm.h
@@ -0,0 +1,22 @@
+/*
+ *
+ * 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 _JZ4740_PCM_H
+#define _JZ4740_PCM_H
+
+#include <linux/dma-mapping.h>
+#include <asm/mach-jz4740/dma.h>
+
+/* platform data */
+extern struct snd_soc_platform jz4740_soc_platform;
+
+struct jz4740_pcm_config {
+	struct jz4740_dma_config dma_config;
+	phys_addr_t fifo_addr;
+};
+
+#endif
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
new file mode 100644
index 0000000..f15f491
--- /dev/null
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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.
+ *
+ *  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/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 <linux/gpio.h>
+
+#include "../codecs/jz4740.h"
+#include "jz4740-pcm.h"
+#include "jz4740-i2s.h"
+
+
+#define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29)
+#define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4)
+
+static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
+			     struct snd_kcontrol *ctrl, int event)
+{
+	int on = 0;
+	if (event & SND_SOC_DAPM_POST_PMU)
+		on = 1;
+	else if (event & SND_SOC_DAPM_PRE_PMD)
+		on = 0;
+
+	gpio_set_value(QI_LB60_SND_GPIO, on);
+	gpio_set_value(QI_LB60_AMP_GPIO, on);
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget qi_lb60_widgets[] = {
+	SND_SOC_DAPM_SPK("Speaker", qi_lb60_spk_event),
+	SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route qi_lb60_routes[] = {
+	{"Mic", NULL, "MIC"},
+	{"Speaker", NULL, "LOUT"},
+	{"Speaker", NULL, "ROUT"},
+};
+
+#define QI_LB60_DAIFMT (SND_SOC_DAIFMT_I2S | \
+			SND_SOC_DAIFMT_NB_NF | \
+			SND_SOC_DAIFMT_CBM_CFM)
+
+static int qi_lb60_codec_init(struct snd_soc_codec *codec)
+{
+	int ret;
+	struct snd_soc_dai *cpu_dai = codec->socdev->card->dai_link->cpu_dai;
+
+	snd_soc_dapm_nc_pin(codec, "LIN");
+	snd_soc_dapm_nc_pin(codec, "RIN");
+
+	ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret);
+		return ret;
+	}
+
+	snd_soc_dapm_new_controls(codec, qi_lb60_widgets, ARRAY_SIZE(qi_lb60_widgets));
+	snd_soc_dapm_add_routes(codec, qi_lb60_routes, ARRAY_SIZE(qi_lb60_routes));
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_dai_link qi_lb60_dai = {
+	.name = "jz4740",
+	.stream_name = "jz4740",
+	.cpu_dai = &jz4740_i2s_dai,
+	.codec_dai = &jz4740_codec_dai,
+	.init = qi_lb60_codec_init,
+};
+
+static struct snd_soc_card qi_lb60 = {
+	.name = "QI LB60",
+	.dai_link = &qi_lb60_dai,
+	.num_links = 1,
+	.platform = &jz4740_soc_platform,
+};
+
+static struct snd_soc_device qi_lb60_snd_devdata = {
+	.card = &qi_lb60,
+	.codec_dev = &soc_codec_dev_jz4740_codec,
+};
+
+static struct platform_device *qi_lb60_snd_device;
+
+static int __init qi_lb60_init(void)
+{
+	int ret;
+
+	qi_lb60_snd_device = platform_device_alloc("soc-audio", -1);
+
+	if (!qi_lb60_snd_device)
+		return -ENOMEM;
+
+	ret = gpio_request(QI_LB60_SND_GPIO, "SND");
+	if (ret) {
+		pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
+				QI_LB60_SND_GPIO, ret);
+		goto err_device_put;
+	}
+
+	ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
+	if (ret) {
+		pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
+				QI_LB60_AMP_GPIO, ret);
+		goto err_gpio_free_snd;
+	}
+
+	gpio_direction_output(QI_LB60_SND_GPIO, 0);
+	gpio_direction_output(QI_LB60_AMP_GPIO, 0);
+
+	platform_set_drvdata(qi_lb60_snd_device, &qi_lb60_snd_devdata);
+	qi_lb60_snd_devdata.dev = &qi_lb60_snd_device->dev;
+
+	ret = platform_device_add(qi_lb60_snd_device);
+	if (ret) {
+		pr_err("qi_lb60 snd: Failed to add snd soc device: %d\n", ret);
+		goto err_unset_pdata;
+	}
+
+	 return 0;
+
+err_unset_pdata:
+	platform_set_drvdata(qi_lb60_snd_device, NULL);
+/*err_gpio_free_amp:*/
+	gpio_free(QI_LB60_AMP_GPIO);
+err_gpio_free_snd:
+	gpio_free(QI_LB60_SND_GPIO);
+err_device_put:
+	platform_device_put(qi_lb60_snd_device);
+
+	return ret;
+}
+module_init(qi_lb60_init);
+
+static void __exit qi_lb60_exit(void)
+{
+	gpio_free(QI_LB60_AMP_GPIO);
+	gpio_free(QI_LB60_SND_GPIO);
+	platform_device_unregister(qi_lb60_snd_device);
+}
+module_exit(qi_lb60_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC QI LB60 Audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
new file mode 100644
index 0000000..16ec2a2
--- /dev/null
+++ b/sound/soc/kirkwood/Kconfig
@@ -0,0 +1,20 @@
+config SND_KIRKWOOD_SOC
+	tristate "SoC Audio for the Marvell Kirkwood chip"
+	depends on ARCH_KIRKWOOD
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the Kirkwood I2S interface. You will also need to select the
+	  audio interfaces to support below.
+
+config SND_KIRKWOOD_SOC_I2S
+	tristate
+
+config SND_KIRKWOOD_SOC_OPENRD
+	tristate "SoC Audio support for Kirkwood Openrd Client"
+	depends on SND_KIRKWOOD_SOC && MACH_OPENRD_CLIENT
+	select SND_KIRKWOOD_SOC_I2S
+	select SND_SOC_CS42L51
+	help
+	  Say Y if you want to add support for SoC audio on
+	  Openrd Client.
+
diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile
new file mode 100644
index 0000000..33a16dc
--- /dev/null
+++ b/sound/soc/kirkwood/Makefile
@@ -0,0 +1,9 @@
+snd-soc-kirkwood-objs := kirkwood-dma.o
+snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o
+
+obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
+obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o
+
+snd-soc-openrd-objs := kirkwood-openrd.o
+
+obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
new file mode 100644
index 0000000..a30205b
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -0,0 +1,383 @@
+/*
+ * kirkwood-dma.c
+ *
+ * (c) 2010 Arnaud Patard <apatard@mandriva.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/mbus.h>
+#include <sound/soc.h>
+#include "kirkwood-dma.h"
+#include "kirkwood.h"
+
+#define KIRKWOOD_RATES \
+	(SNDRV_PCM_RATE_44100 | \
+	 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+#define KIRKWOOD_FORMATS \
+	(SNDRV_PCM_FMTBIT_S16_LE | \
+	 SNDRV_PCM_FMTBIT_S24_LE | \
+	 SNDRV_PCM_FMTBIT_S32_LE)
+
+struct kirkwood_dma_priv {
+	struct snd_pcm_substream *play_stream;
+	struct snd_pcm_substream *rec_stream;
+	struct kirkwood_dma_data *data;
+};
+
+static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		 SNDRV_PCM_INFO_PAUSE),
+	.formats		= KIRKWOOD_FORMATS,
+	.rates			= KIRKWOOD_RATES,
+	.rate_min		= 44100,
+	.rate_max		= 96000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS,
+	.period_bytes_min	= KIRKWOOD_SND_MIN_PERIOD_BYTES,
+	.period_bytes_max	= KIRKWOOD_SND_MAX_PERIOD_BYTES,
+	.periods_min		= KIRKWOOD_SND_MIN_PERIODS,
+	.periods_max		= KIRKWOOD_SND_MAX_PERIODS,
+	.fifo_size		= 0,
+};
+
+static u64 kirkwood_dma_dmamask = 0xFFFFFFFFUL;
+
+static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
+{
+	struct kirkwood_dma_priv *prdata = dev_id;
+	struct kirkwood_dma_data *priv = prdata->data;
+	unsigned long mask, status, cause;
+
+	mask = readl(priv->io + KIRKWOOD_INT_MASK);
+	status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask;
+
+	cause = readl(priv->io + KIRKWOOD_ERR_CAUSE);
+	if (unlikely(cause)) {
+		printk(KERN_WARNING "%s: got err interrupt 0x%lx\n",
+				__func__, cause);
+		writel(cause, priv->io + KIRKWOOD_ERR_CAUSE);
+		return IRQ_HANDLED;
+	}
+
+	/* we've enabled only bytes interrupts ... */
+	if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
+			KIRKWOOD_INT_CAUSE_REC_BYTES)) {
+		printk(KERN_WARNING "%s: unexpected interrupt %lx\n",
+			__func__, status);
+		return IRQ_NONE;
+	}
+
+	/* ack int */
+	writel(status, priv->io + KIRKWOOD_INT_CAUSE);
+
+	if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
+		snd_pcm_period_elapsed(prdata->play_stream);
+
+	if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
+		snd_pcm_period_elapsed(prdata->rec_stream);
+
+	return IRQ_HANDLED;
+}
+
+static void kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
+					unsigned long dma,
+					struct mbus_dram_target_info *dram)
+{
+	int i;
+
+	/* First disable and clear windows */
+	writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
+	writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
+
+	/* try to find matching cs for current dma address */
+	for (i = 0; i < dram->num_cs; i++) {
+		struct mbus_dram_window *cs = dram->cs + i;
+		if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
+			writel(cs->base & 0xffff0000,
+				base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
+			writel(((cs->size - 1) & 0xffff0000) |
+				(cs->mbus_attr << 8) |
+				(dram->mbus_dram_target_id << 4) | 1,
+				base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
+		}
+	}
+}
+
+static int kirkwood_dma_open(struct snd_pcm_substream *substream)
+{
+	int err;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
+	struct kirkwood_dma_data *priv;
+	struct kirkwood_dma_priv *prdata = cpu_dai->private_data;
+	unsigned long addr;
+
+	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
+	snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
+
+	/* Ensure that all constraints linked to dma burst are fullfilled */
+	err = snd_pcm_hw_constraint_minmax(runtime,
+			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			priv->burst * 2,
+			KIRKWOOD_AUDIO_BUF_MAX-1);
+	if (err < 0)
+		return err;
+
+	err = snd_pcm_hw_constraint_step(runtime, 0,
+			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			priv->burst);
+	if (err < 0)
+		return err;
+
+	err = snd_pcm_hw_constraint_step(substream->runtime, 0,
+			 SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+			 priv->burst);
+	if (err < 0)
+		return err;
+
+	if (soc_runtime->dai->cpu_dai->private_data == NULL) {
+		prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL);
+		if (prdata == NULL)
+			return -ENOMEM;
+
+		prdata->data = priv;
+
+		err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
+				  "kirkwood-i2s", prdata);
+		if (err) {
+			kfree(prdata);
+			return -EBUSY;
+		}
+
+		soc_runtime->dai->cpu_dai->private_data = prdata;
+
+		/*
+		 * Enable Error interrupts. We're only ack'ing them but
+		 * it's usefull for diagnostics
+		 */
+		writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK);
+	}
+
+	addr = virt_to_phys(substream->dma_buffer.area);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		prdata->play_stream = substream;
+		kirkwood_dma_conf_mbus_windows(priv->io,
+			KIRKWOOD_PLAYBACK_WIN, addr, priv->dram);
+	} else {
+		prdata->rec_stream = substream;
+		kirkwood_dma_conf_mbus_windows(priv->io,
+			KIRKWOOD_RECORD_WIN, addr, priv->dram);
+	}
+
+	return 0;
+}
+
+static int kirkwood_dma_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
+	struct kirkwood_dma_priv *prdata = cpu_dai->private_data;
+	struct kirkwood_dma_data *priv;
+
+	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+	if (!prdata || !priv)
+		return 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prdata->play_stream = NULL;
+	else
+		prdata->rec_stream = NULL;
+
+	if (!prdata->play_stream && !prdata->rec_stream) {
+		writel(0, priv->io + KIRKWOOD_ERR_MASK);
+		free_irq(priv->irq, prdata);
+		kfree(prdata);
+		soc_runtime->dai->cpu_dai->private_data = NULL;
+	}
+
+	return 0;
+}
+
+static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	return 0;
+}
+
+static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
+	struct kirkwood_dma_data *priv;
+	unsigned long size, count;
+
+	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+	/* compute buffer size in term of "words" as requested in specs */
+	size = frames_to_bytes(runtime, runtime->buffer_size);
+	size = (size>>2)-1;
+	count = snd_pcm_lib_period_bytes(substream);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
+		writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
+		writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
+	} else {
+		writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
+		writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
+		writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
+	}
+
+
+	return 0;
+}
+
+static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
+						*substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
+	struct kirkwood_dma_data *priv;
+	snd_pcm_uframes_t count;
+
+	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		count = bytes_to_frames(substream->runtime,
+			readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
+	else
+		count = bytes_to_frames(substream->runtime,
+			readl(priv->io + KIRKWOOD_REC_BYTE_COUNT));
+
+	return count;
+}
+
+struct snd_pcm_ops kirkwood_dma_ops = {
+	.open =		kirkwood_dma_open,
+	.close =        kirkwood_dma_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	kirkwood_dma_hw_params,
+	.hw_free =      kirkwood_dma_hw_free,
+	.prepare =      kirkwood_dma_prepare,
+	.pointer =	kirkwood_dma_pointer,
+};
+
+static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
+		int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+			&buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+	buf->bytes = size;
+	buf->private_data = NULL;
+
+	return 0;
+}
+
+static int kirkwood_dma_new(struct snd_card *card,
+		struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+	int ret;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &kirkwood_dma_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (dai->playback.channels_min) {
+		ret = kirkwood_dma_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			return ret;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = kirkwood_dma_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+				buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+struct snd_soc_platform kirkwood_soc_platform = {
+	.name		= "kirkwood-dma",
+	.pcm_ops	= &kirkwood_dma_ops,
+	.pcm_new	= kirkwood_dma_new,
+	.pcm_free	= kirkwood_dma_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(kirkwood_soc_platform);
+
+static int __init kirkwood_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&kirkwood_soc_platform);
+}
+module_init(kirkwood_soc_platform_init);
+
+static void __exit kirkwood_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&kirkwood_soc_platform);
+}
+module_exit(kirkwood_soc_platform_exit);
+
+MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
+MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/kirkwood/kirkwood-dma.h b/sound/soc/kirkwood/kirkwood-dma.h
new file mode 100644
index 0000000..ba4454c
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-dma.h
@@ -0,0 +1,17 @@
+/*
+ * kirkwood-dma.h
+ *
+ * (c) 2010 Arnaud Patard <apatard@mandriva.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 _KIRKWOOD_DMA_H
+#define _KIRKWOOD_DMA_H
+
+extern struct snd_soc_platform kirkwood_soc_platform;
+
+#endif
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
new file mode 100644
index 0000000..981ffc2
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -0,0 +1,495 @@
+/*
+ * kirkwood-i2s.c
+ *
+ * (c) 2010 Arnaud Patard <apatard@mandriva.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/mbus.h>
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <plat/audio.h>
+#include "kirkwood-i2s.h"
+#include "kirkwood.h"
+
+#define DRV_NAME	"kirkwood-i2s"
+
+#define KIRKWOOD_I2S_RATES \
+	(SNDRV_PCM_RATE_44100 | \
+	 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+#define KIRKWOOD_I2S_FORMATS \
+	(SNDRV_PCM_FMTBIT_S16_LE | \
+	 SNDRV_PCM_FMTBIT_S24_LE | \
+	 SNDRV_PCM_FMTBIT_S32_LE)
+
+
+struct snd_soc_dai kirkwood_i2s_dai;
+static struct kirkwood_dma_data *priv;
+
+static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+		unsigned int fmt)
+{
+	unsigned long mask;
+	unsigned long value;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_RIGHT_J:
+		mask = KIRKWOOD_I2S_CTL_RJ;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		mask = KIRKWOOD_I2S_CTL_LJ;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		mask = KIRKWOOD_I2S_CTL_I2S;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * Set same format for playback and record
+	 * This avoids some troubles.
+	 */
+	value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
+	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
+	value |= mask;
+	writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
+
+	value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
+	value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
+	value |= mask;
+	writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
+
+	return 0;
+}
+
+static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
+{
+	unsigned long value;
+
+	value = KIRKWOOD_DCO_CTL_OFFSET_0;
+	switch (rate) {
+	default:
+	case 44100:
+		value |= KIRKWOOD_DCO_CTL_FREQ_11;
+		break;
+	case 48000:
+		value |= KIRKWOOD_DCO_CTL_FREQ_12;
+		break;
+	case 96000:
+		value |= KIRKWOOD_DCO_CTL_FREQ_24;
+		break;
+	}
+	writel(value, io + KIRKWOOD_DCO_CTL);
+
+	/* wait for dco locked */
+	do {
+		cpu_relax();
+		value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
+		value &= KIRKWOOD_DCO_SPCR_STATUS;
+	} while (value == 0);
+}
+
+static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	unsigned int i2s_reg, reg;
+	unsigned long i2s_value, value;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		i2s_reg = KIRKWOOD_I2S_PLAYCTL;
+		reg = KIRKWOOD_PLAYCTL;
+	} else {
+		i2s_reg = KIRKWOOD_I2S_RECCTL;
+		reg = KIRKWOOD_RECCTL;
+	}
+
+	/* set dco conf */
+	kirkwood_set_dco(priv->io, params_rate(params));
+
+	i2s_value = readl(priv->io+i2s_reg);
+	i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
+
+	value = readl(priv->io+reg);
+	value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK;
+
+	/*
+	 * Size settings in play/rec i2s control regs and play/rec control
+	 * regs must be the same.
+	 */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
+		value |= KIRKWOOD_PLAYCTL_SIZE_16_C;
+		break;
+	/*
+	 * doesn't work... S20_3LE != kirkwood 20bit format ?
+	 *
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
+		value |= KIRKWOOD_PLAYCTL_SIZE_20;
+		break;
+	*/
+	case SNDRV_PCM_FORMAT_S24_LE:
+		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
+		value |= KIRKWOOD_PLAYCTL_SIZE_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
+		value |= KIRKWOOD_PLAYCTL_SIZE_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		value &= ~KIRKWOOD_PLAYCTL_MONO_MASK;
+		if (params_channels(params) == 1)
+			value |= KIRKWOOD_PLAYCTL_MONO_BOTH;
+		else
+			value |= KIRKWOOD_PLAYCTL_MONO_OFF;
+	}
+
+	writel(i2s_value, priv->io+i2s_reg);
+	writel(value, priv->io+reg);
+
+	return 0;
+}
+
+static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
+				int cmd, struct snd_soc_dai *dai)
+{
+	unsigned long value;
+
+	/*
+	 * specs says KIRKWOOD_PLAYCTL must be read 2 times before
+	 * changing it. So read 1 time here and 1 later.
+	 */
+	value = readl(priv->io + KIRKWOOD_PLAYCTL);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* stop audio, enable interrupts */
+		value = readl(priv->io + KIRKWOOD_PLAYCTL);
+		value |= KIRKWOOD_PLAYCTL_PAUSE;
+		writel(value, priv->io + KIRKWOOD_PLAYCTL);
+
+		value = readl(priv->io + KIRKWOOD_INT_MASK);
+		value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
+		writel(value, priv->io + KIRKWOOD_INT_MASK);
+
+		/* configure audio & enable i2s playback */
+		value = readl(priv->io + KIRKWOOD_PLAYCTL);
+		value &= ~KIRKWOOD_PLAYCTL_BURST_MASK;
+		value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE
+				| KIRKWOOD_PLAYCTL_SPDIF_EN);
+
+		if (priv->burst == 32)
+			value |= KIRKWOOD_PLAYCTL_BURST_32;
+		else
+			value |= KIRKWOOD_PLAYCTL_BURST_128;
+		value |= KIRKWOOD_PLAYCTL_I2S_EN;
+		writel(value, priv->io + KIRKWOOD_PLAYCTL);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		/* stop audio, disable interrupts */
+		value = readl(priv->io + KIRKWOOD_PLAYCTL);
+		value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
+		writel(value, priv->io + KIRKWOOD_PLAYCTL);
+
+		value = readl(priv->io + KIRKWOOD_INT_MASK);
+		value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
+		writel(value, priv->io + KIRKWOOD_INT_MASK);
+
+		/* disable all playbacks */
+		value = readl(priv->io + KIRKWOOD_PLAYCTL);
+		value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
+		writel(value, priv->io + KIRKWOOD_PLAYCTL);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		value = readl(priv->io + KIRKWOOD_PLAYCTL);
+		value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
+		writel(value, priv->io + KIRKWOOD_PLAYCTL);
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		value = readl(priv->io + KIRKWOOD_PLAYCTL);
+		value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE);
+		writel(value, priv->io + KIRKWOOD_PLAYCTL);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
+				int cmd, struct snd_soc_dai *dai)
+{
+	unsigned long value;
+
+	value = readl(priv->io + KIRKWOOD_RECCTL);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* stop audio, enable interrupts */
+		value = readl(priv->io + KIRKWOOD_RECCTL);
+		value |= KIRKWOOD_RECCTL_PAUSE;
+		writel(value, priv->io + KIRKWOOD_RECCTL);
+
+		value = readl(priv->io + KIRKWOOD_INT_MASK);
+		value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
+		writel(value, priv->io + KIRKWOOD_INT_MASK);
+
+		/* configure audio & enable i2s record */
+		value = readl(priv->io + KIRKWOOD_RECCTL);
+		value &= ~KIRKWOOD_RECCTL_BURST_MASK;
+		value &= ~KIRKWOOD_RECCTL_MONO;
+		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE
+			| KIRKWOOD_RECCTL_SPDIF_EN);
+
+		if (priv->burst == 32)
+			value |= KIRKWOOD_RECCTL_BURST_32;
+		else
+			value |= KIRKWOOD_RECCTL_BURST_128;
+		value |= KIRKWOOD_RECCTL_I2S_EN;
+
+		writel(value, priv->io + KIRKWOOD_RECCTL);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		/* stop audio, disable interrupts */
+		value = readl(priv->io + KIRKWOOD_RECCTL);
+		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
+		writel(value, priv->io + KIRKWOOD_RECCTL);
+
+		value = readl(priv->io + KIRKWOOD_INT_MASK);
+		value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
+		writel(value, priv->io + KIRKWOOD_INT_MASK);
+
+		/* disable all records */
+		value = readl(priv->io + KIRKWOOD_RECCTL);
+		value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
+		writel(value, priv->io + KIRKWOOD_RECCTL);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		value = readl(priv->io + KIRKWOOD_RECCTL);
+		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
+		writel(value, priv->io + KIRKWOOD_RECCTL);
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		value = readl(priv->io + KIRKWOOD_RECCTL);
+		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
+		writel(value, priv->io + KIRKWOOD_RECCTL);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			       struct snd_soc_dai *dai)
+{
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return kirkwood_i2s_play_trigger(substream, cmd, dai);
+	else
+		return kirkwood_i2s_rec_trigger(substream, cmd, dai);
+
+	return 0;
+}
+
+static int kirkwood_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
+{
+	unsigned long value;
+	unsigned int reg_data;
+
+	/* put system in a "safe" state : */
+	/* disable audio interrupts */
+	writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
+	writel(0, priv->io + KIRKWOOD_INT_MASK);
+
+	reg_data = readl(priv->io + 0x1200);
+	reg_data &= (~(0x333FF8));
+	reg_data |= 0x111D18;
+	writel(reg_data, priv->io + 0x1200);
+
+	msleep(500);
+
+	reg_data = readl(priv->io + 0x1200);
+	reg_data &= (~(0x333FF8));
+	reg_data |= 0x111D18;
+	writel(reg_data, priv->io + 0x1200);
+
+	/* disable playback/record */
+	value = readl(priv->io + KIRKWOOD_PLAYCTL);
+	value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
+	writel(value, priv->io + KIRKWOOD_PLAYCTL);
+
+	value = readl(priv->io + KIRKWOOD_RECCTL);
+	value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
+	writel(value, priv->io + KIRKWOOD_RECCTL);
+
+	return 0;
+
+}
+
+static void kirkwood_i2s_remove(struct platform_device *pdev,
+				struct snd_soc_dai *dai)
+{
+}
+
+static struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
+	.trigger	= kirkwood_i2s_trigger,
+	.hw_params      = kirkwood_i2s_hw_params,
+	.set_fmt        = kirkwood_i2s_set_fmt,
+};
+
+
+struct snd_soc_dai kirkwood_i2s_dai = {
+	.name = DRV_NAME,
+	.id = 0,
+	.probe = kirkwood_i2s_probe,
+	.remove = kirkwood_i2s_remove,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = KIRKWOOD_I2S_RATES,
+		.formats = KIRKWOOD_I2S_FORMATS,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = KIRKWOOD_I2S_RATES,
+		.formats = KIRKWOOD_I2S_FORMATS,},
+	.ops = &kirkwood_i2s_dai_ops,
+};
+EXPORT_SYMBOL_GPL(kirkwood_i2s_dai);
+
+static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev)
+{
+	struct resource *mem;
+	struct kirkwood_asoc_platform_data *data =
+		pdev->dev.platform_data;
+	int err;
+
+	priv = kzalloc(sizeof(struct kirkwood_dma_data), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "allocation failed\n");
+		err = -ENOMEM;
+		goto error;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "platform_get_resource failed\n");
+		err = -ENXIO;
+		goto err_alloc;
+	}
+
+	priv->mem = request_mem_region(mem->start, SZ_16K, DRV_NAME);
+	if (!priv->mem) {
+		dev_err(&pdev->dev, "request_mem_region failed\n");
+		err = -EBUSY;
+		goto error;
+	}
+
+	priv->io = ioremap(priv->mem->start, SZ_16K);
+	if (!priv->io) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		err = -ENOMEM;
+		goto err_iomem;
+	}
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq <= 0) {
+		dev_err(&pdev->dev, "platform_get_irq failed\n");
+		err = -ENXIO;
+		goto err_ioremap;
+	}
+
+	if (!data || !data->dram) {
+		dev_err(&pdev->dev, "no platform data ?!\n");
+		err = -EINVAL;
+		goto err_ioremap;
+	}
+
+	priv->dram = data->dram;
+	priv->burst = data->burst;
+
+	kirkwood_i2s_dai.capture.dma_data = priv;
+	kirkwood_i2s_dai.playback.dma_data = priv;
+
+	return snd_soc_register_dai(&kirkwood_i2s_dai);
+
+err_ioremap:
+	iounmap(priv->io);
+err_iomem:
+	release_mem_region(priv->mem->start, SZ_16K);
+err_alloc:
+	kfree(priv);
+error:
+	return err;
+}
+
+static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev)
+{
+	if (priv) {
+		iounmap(priv->io);
+		release_mem_region(priv->mem->start, SZ_16K);
+		kfree(priv);
+	}
+	snd_soc_unregister_dai(&kirkwood_i2s_dai);
+	return 0;
+}
+
+static struct platform_driver kirkwood_i2s_driver = {
+	.probe  = kirkwood_i2s_dev_probe,
+	.remove = kirkwood_i2s_dev_remove,
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init kirkwood_i2s_init(void)
+{
+	return platform_driver_register(&kirkwood_i2s_driver);
+}
+module_init(kirkwood_i2s_init);
+
+static void __exit kirkwood_i2s_exit(void)
+{
+	platform_driver_unregister(&kirkwood_i2s_driver);
+}
+module_exit(kirkwood_i2s_exit);
+
+/* Module information */
+MODULE_AUTHOR("Arnaud Patard, <apatard@mandriva.com>");
+MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kirkwood-i2s");
diff --git a/sound/soc/kirkwood/kirkwood-i2s.h b/sound/soc/kirkwood/kirkwood-i2s.h
new file mode 100644
index 0000000..c5595c6
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-i2s.h
@@ -0,0 +1,17 @@
+/*
+ * kirkwood-i2s.h
+ *
+ * (c) 2010 Arnaud Patard <apatard@mandriva.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 _KIRKWOOD_I2S_H
+#define _KIRKWOOD_I2S_H
+
+extern struct snd_soc_dai kirkwood_i2s_dai;
+
+#endif
diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c
new file mode 100644
index 0000000..0353d06
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-openrd.c
@@ -0,0 +1,126 @@
+/*
+ * kirkwood-openrd.c
+ *
+ * (c) 2010 Arnaud Patard <apatard@mandriva.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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <mach/kirkwood.h>
+#include <plat/audio.h>
+#include <asm/mach-types.h>
+#include "kirkwood-i2s.h"
+#include "kirkwood-dma.h"
+#include "../codecs/cs42l51.h"
+
+static int openrd_client_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;
+	int ret;
+	unsigned int freq, fmt;
+
+	fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+	if (ret < 0)
+		return ret;
+
+	switch (params_rate(params)) {
+	default:
+	case 44100:
+		freq = 11289600;
+		break;
+	case 48000:
+		freq = 12288000;
+		break;
+	case 96000:
+		freq = 24576000;
+		break;
+	}
+
+	return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
+
+}
+
+static struct snd_soc_ops openrd_client_ops = {
+	.hw_params = openrd_client_hw_params,
+};
+
+
+static struct snd_soc_dai_link openrd_client_dai[] = {
+{
+	.name = "CS42L51",
+	.stream_name = "CS42L51 HiFi",
+	.cpu_dai = &kirkwood_i2s_dai,
+	.codec_dai = &cs42l51_dai,
+	.ops = &openrd_client_ops,
+},
+};
+
+
+static struct snd_soc_card openrd_client = {
+	.name = "OpenRD Client",
+	.platform = &kirkwood_soc_platform,
+	.dai_link = openrd_client_dai,
+	.num_links = ARRAY_SIZE(openrd_client_dai),
+};
+
+static struct snd_soc_device openrd_client_snd_devdata = {
+	.card = &openrd_client,
+	.codec_dev = &soc_codec_device_cs42l51,
+};
+
+static struct platform_device *openrd_client_snd_device;
+
+static int __init openrd_client_init(void)
+{
+	int ret;
+
+	if (!machine_is_openrd_client())
+		return 0;
+
+	openrd_client_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!openrd_client_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(openrd_client_snd_device,
+			&openrd_client_snd_devdata);
+	openrd_client_snd_devdata.dev = &openrd_client_snd_device->dev;
+
+	ret = platform_device_add(openrd_client_snd_device);
+	if (ret) {
+		printk(KERN_ERR "%s: platform_device_add failed\n", __func__);
+		platform_device_put(openrd_client_snd_device);
+	}
+
+	return ret;
+}
+
+static void __exit openrd_client_exit(void)
+{
+	platform_device_unregister(openrd_client_snd_device);
+}
+
+module_init(openrd_client_init);
+module_exit(openrd_client_exit);
+
+/* Module information */
+MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
+MODULE_DESCRIPTION("ALSA SoC OpenRD Client");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-audio");
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
new file mode 100644
index 0000000..bb6e6a5
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood.h
@@ -0,0 +1,129 @@
+/*
+ * kirkwood.h
+ *
+ * (c) 2010 Arnaud Patard <apatard@mandriva.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 _KIRKWOOD_AUDIO_H
+#define _KIRKWOOD_AUDIO_H
+
+#define KIRKWOOD_RECORD_WIN			0
+#define KIRKWOOD_PLAYBACK_WIN			1
+#define KIRKWOOD_MAX_AUDIO_WIN			2
+
+#define KIRKWOOD_AUDIO_WIN_BASE_REG(win)	(0xA00 + ((win)<<3))
+#define KIRKWOOD_AUDIO_WIN_CTRL_REG(win)	(0xA04 + ((win)<<3))
+
+
+#define KIRKWOOD_RECCTL			0x1000
+#define KIRKWOOD_RECCTL_SPDIF_EN		(1<<11)
+#define KIRKWOOD_RECCTL_I2S_EN			(1<<10)
+#define KIRKWOOD_RECCTL_PAUSE			(1<<9)
+#define KIRKWOOD_RECCTL_MUTE			(1<<8)
+#define KIRKWOOD_RECCTL_BURST_MASK		(3<<5)
+#define KIRKWOOD_RECCTL_BURST_128		(2<<5)
+#define KIRKWOOD_RECCTL_BURST_32		(1<<5)
+#define KIRKWOOD_RECCTL_MONO			(1<<4)
+#define KIRKWOOD_RECCTL_MONO_CHAN_RIGHT	(1<<3)
+#define KIRKWOOD_RECCTL_MONO_CHAN_LEFT		(0<<3)
+#define KIRKWOOD_RECCTL_SIZE_MASK		(7<<0)
+#define KIRKWOOD_RECCTL_SIZE_16		(7<<0)
+#define KIRKWOOD_RECCTL_SIZE_16_C		(3<<0)
+#define KIRKWOOD_RECCTL_SIZE_20		(2<<0)
+#define KIRKWOOD_RECCTL_SIZE_24		(1<<0)
+#define KIRKWOOD_RECCTL_SIZE_32		(0<<0)
+
+#define KIRKWOOD_REC_BUF_ADDR			0x1004
+#define KIRKWOOD_REC_BUF_SIZE			0x1008
+#define KIRKWOOD_REC_BYTE_COUNT			0x100C
+
+#define KIRKWOOD_PLAYCTL			0x1100
+#define KIRKWOOD_PLAYCTL_PLAY_BUSY		(1<<16)
+#define KIRKWOOD_PLAYCTL_BURST_MASK		(3<<11)
+#define KIRKWOOD_PLAYCTL_BURST_128		(2<<11)
+#define KIRKWOOD_PLAYCTL_BURST_32		(1<<11)
+#define KIRKWOOD_PLAYCTL_PAUSE			(1<<9)
+#define KIRKWOOD_PLAYCTL_SPDIF_MUTE		(1<<8)
+#define KIRKWOOD_PLAYCTL_MONO_MASK		(3<<5)
+#define KIRKWOOD_PLAYCTL_MONO_BOTH		(3<<5)
+#define KIRKWOOD_PLAYCTL_MONO_OFF		(0<<5)
+#define KIRKWOOD_PLAYCTL_I2S_MUTE		(1<<7)
+#define KIRKWOOD_PLAYCTL_SPDIF_EN		(1<<4)
+#define KIRKWOOD_PLAYCTL_I2S_EN		(1<<3)
+#define KIRKWOOD_PLAYCTL_SIZE_MASK		(7<<0)
+#define KIRKWOOD_PLAYCTL_SIZE_16		(7<<0)
+#define KIRKWOOD_PLAYCTL_SIZE_16_C		(3<<0)
+#define KIRKWOOD_PLAYCTL_SIZE_20		(2<<0)
+#define KIRKWOOD_PLAYCTL_SIZE_24		(1<<0)
+#define KIRKWOOD_PLAYCTL_SIZE_32		(0<<0)
+
+#define KIRKWOOD_PLAY_BUF_ADDR			0x1104
+#define KIRKWOOD_PLAY_BUF_SIZE			0x1108
+#define KIRKWOOD_PLAY_BYTE_COUNT		0x110C
+
+#define KIRKWOOD_DCO_CTL			0x1204
+#define KIRKWOOD_DCO_CTL_OFFSET_MASK		(0xFFF<<2)
+#define KIRKWOOD_DCO_CTL_OFFSET_0		(0x800<<2)
+#define KIRKWOOD_DCO_CTL_FREQ_MASK		(3<<0)
+#define KIRKWOOD_DCO_CTL_FREQ_11		(0<<0)
+#define KIRKWOOD_DCO_CTL_FREQ_12		(1<<0)
+#define KIRKWOOD_DCO_CTL_FREQ_24		(2<<0)
+
+#define KIRKWOOD_DCO_SPCR_STATUS		0x120c
+#define KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK	(1<<16)
+
+#define KIRKWOOD_ERR_CAUSE			0x1300
+#define KIRKWOOD_ERR_MASK			0x1304
+
+#define KIRKWOOD_INT_CAUSE			0x1308
+#define KIRKWOOD_INT_MASK			0x130C
+#define KIRKWOOD_INT_CAUSE_PLAY_BYTES		(1<<14)
+#define KIRKWOOD_INT_CAUSE_REC_BYTES		(1<<13)
+#define KIRKWOOD_INT_CAUSE_DMA_PLAY_END	(1<<7)
+#define KIRKWOOD_INT_CAUSE_DMA_PLAY_3Q		(1<<6)
+#define KIRKWOOD_INT_CAUSE_DMA_PLAY_HALF	(1<<5)
+#define KIRKWOOD_INT_CAUSE_DMA_PLAY_1Q		(1<<4)
+#define KIRKWOOD_INT_CAUSE_DMA_REC_END		(1<<3)
+#define KIRKWOOD_INT_CAUSE_DMA_REC_3Q		(1<<2)
+#define KIRKWOOD_INT_CAUSE_DMA_REC_HALF	(1<<1)
+#define KIRKWOOD_INT_CAUSE_DMA_REC_1Q		(1<<0)
+
+#define KIRKWOOD_REC_BYTE_INT_COUNT		0x1310
+#define KIRKWOOD_PLAY_BYTE_INT_COUNT		0x1314
+#define KIRKWOOD_BYTE_INT_COUNT_MASK		0xffffff
+
+#define KIRKWOOD_I2S_PLAYCTL			0x2508
+#define KIRKWOOD_I2S_RECCTL			0x2408
+#define KIRKWOOD_I2S_CTL_JUST_MASK		(0xf<<26)
+#define KIRKWOOD_I2S_CTL_LJ			(0<<26)
+#define KIRKWOOD_I2S_CTL_I2S			(5<<26)
+#define KIRKWOOD_I2S_CTL_RJ			(8<<26)
+#define KIRKWOOD_I2S_CTL_SIZE_MASK		(3<<30)
+#define KIRKWOOD_I2S_CTL_SIZE_16		(3<<30)
+#define KIRKWOOD_I2S_CTL_SIZE_20		(2<<30)
+#define KIRKWOOD_I2S_CTL_SIZE_24		(1<<30)
+#define KIRKWOOD_I2S_CTL_SIZE_32		(0<<30)
+
+#define KIRKWOOD_AUDIO_BUF_MAX			(16*1024*1024)
+
+/* Theses values come from the marvell alsa driver */
+/* need to find where they come from               */
+#define KIRKWOOD_SND_MIN_PERIODS		8
+#define KIRKWOOD_SND_MAX_PERIODS		16
+#define KIRKWOOD_SND_MIN_PERIOD_BYTES		0x4000
+#define KIRKWOOD_SND_MAX_PERIOD_BYTES		0x4000
+
+struct kirkwood_dma_data {
+	struct resource *mem;
+	void __iomem *io;
+	int irq;
+	int burst;
+	struct mbus_dram_target_info *dram;
+};
+
+#endif
diff --git a/sound/soc/nuc900/Kconfig b/sound/soc/nuc900/Kconfig
new file mode 100644
index 0000000..a0ed1c6
--- /dev/null
+++ b/sound/soc/nuc900/Kconfig
@@ -0,0 +1,27 @@
+##
+## NUC900 series AC97 API
+##
+config SND_SOC_NUC900
+	tristate "SoC Audio for NUC900 series"
+	depends on ARCH_W90X900
+	help
+	  This option enables support for AC97 mode on the NUC900 SoC.
+
+config SND_SOC_NUC900_AC97
+	tristate
+	select AC97_BUS
+	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_NUC900EVB
+	tristate "NUC900 AC97 support for demo board"
+	depends on SND_SOC_NUC900
+	select SND_SOC_NUC900_AC97
+	select SND_SOC_AC97_CODEC
+	help
+	  Select this option to enable audio (AC97) on the
+	  NUC900 demoboard.
diff --git a/sound/soc/nuc900/Makefile b/sound/soc/nuc900/Makefile
new file mode 100644
index 0000000..7e46c71
--- /dev/null
+++ b/sound/soc/nuc900/Makefile
@@ -0,0 +1,11 @@
+# NUC900 series audio
+snd-soc-nuc900-pcm-objs := nuc900-pcm.o
+snd-soc-nuc900-ac97-objs := nuc900-ac97.o
+
+obj-$(CONFIG_SND_SOC_NUC900) += snd-soc-nuc900-pcm.o
+obj-$(CONFIG_SND_SOC_NUC900_AC97) += snd-soc-nuc900-ac97.o
+
+# Boards
+snd-soc-nuc900-audio-objs := nuc900-audio.o
+
+obj-$(CONFIG_SND_SOC_NUC900EVB) += snd-soc-nuc900-audio.o
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
new file mode 100644
index 0000000..caa7c90
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2009-2010 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@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;version 2 of the License.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+
+#include <mach/mfp.h>
+
+#include "nuc900-audio.h"
+
+static DEFINE_MUTEX(ac97_mutex);
+struct nuc900_audio *nuc900_ac97_data;
+
+static int nuc900_checkready(void)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+
+	if (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS0) & CODEC_READY))
+		return -EPERM;
+
+	return 0;
+}
+
+/* AC97 controller reads codec register */
+static unsigned short nuc900_ac97_read(struct snd_ac97 *ac97,
+					unsigned short reg)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+	unsigned long timeout = 0x10000, val;
+
+	mutex_lock(&ac97_mutex);
+
+	val = nuc900_checkready();
+	if (!!val) {
+		dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
+		goto out;
+	}
+
+	/* set the R_WB bit and write register index */
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, R_WB | reg);
+
+	/* set the valid frame bit and valid slots */
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
+	val |= (VALID_FRAME | SLOT1_VALID);
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
+
+	udelay(100);
+
+	/* polling the AC_R_FINISH */
+	while (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_R_FINISH)
+								&& timeout--)
+		mdelay(1);
+
+	if (!timeout) {
+		dev_err(nuc900_audio->dev, "AC97 read register time out !\n");
+		val = -EPERM;
+		goto out;
+	}
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0) ;
+	val &= ~SLOT1_VALID;
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
+
+	if (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS1) >> 2 != reg) {
+		dev_err(nuc900_audio->dev,
+				"R_INDEX of REG_ACTL_ACIS1 not match!\n");
+	}
+
+	udelay(100);
+	val = (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS2) & 0xFFFF);
+
+out:
+	mutex_unlock(&ac97_mutex);
+	return val;
+}
+
+/* AC97 controller writes to codec register */
+static void nuc900_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+				unsigned short val)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+	unsigned long tmp, timeout = 0x10000;
+
+	mutex_lock(&ac97_mutex);
+
+	tmp = nuc900_checkready();
+	if (!!tmp)
+		dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
+
+	/* clear the R_WB bit and write register index */
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, reg);
+
+	/* write register value */
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS2, val);
+
+	/* set the valid frame bit and valid slots */
+	tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
+	tmp |= SLOT1_VALID | SLOT2_VALID | VALID_FRAME;
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
+
+	udelay(100);
+
+	/* polling the AC_W_FINISH */
+	while ((AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON) & AC_W_FINISH)
+								&& timeout--)
+		mdelay(1);
+
+	if (!timeout)
+		dev_err(nuc900_audio->dev, "AC97 write register time out !\n");
+
+	tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
+	tmp &= ~(SLOT1_VALID | SLOT2_VALID);
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
+
+	mutex_unlock(&ac97_mutex);
+
+}
+
+static void nuc900_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+	unsigned long val;
+
+	mutex_lock(&ac97_mutex);
+
+	/* warm reset AC 97 */
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
+	val |= AC_W_RES;
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
+
+	udelay(100);
+
+	val = nuc900_checkready();
+	if (!!val)
+		dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
+
+	mutex_unlock(&ac97_mutex);
+}
+
+static void nuc900_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+	unsigned long val;
+
+	mutex_lock(&ac97_mutex);
+
+	/* reset Audio Controller */
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
+	val |= ACTL_RESET_BIT;
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
+	val &= (~ACTL_RESET_BIT);
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+
+	/* reset AC-link interface */
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
+	val |= AC_RESET;
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
+	val &= ~AC_RESET;
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+
+	/* cold reset AC 97 */
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
+	val |= AC_C_RES;
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
+	val &= (~AC_C_RES);
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
+
+	udelay(100);
+
+	mutex_unlock(&ac97_mutex);
+
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read		= nuc900_ac97_read,
+	.write		= nuc900_ac97_write,
+	.reset		= nuc900_ac97_cold_reset,
+	.warm_reset	= nuc900_ac97_warm_reset,
+}
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int nuc900_ac97_trigger(struct snd_pcm_substream *substream,
+				int cmd, struct snd_soc_dai *dai)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+	int ret;
+	unsigned long val, tmp;
+
+	ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
+			tmp |= (SLOT3_VALID | SLOT4_VALID | VALID_FRAME);
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
+
+			tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
+			tmp |= (P_DMA_END_IRQ | P_DMA_MIDDLE_IRQ);
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, tmp);
+			val |= AC_PLAY;
+		} else {
+			tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
+			tmp |= (R_DMA_END_IRQ | R_DMA_MIDDLE_IRQ);
+
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, tmp);
+			val |= AC_RECORD;
+		}
+
+		AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
+			tmp &= ~(SLOT3_VALID | SLOT4_VALID);
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
+
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, RESET_PRSR);
+			val &= ~AC_PLAY;
+		} else {
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, RESET_PRSR);
+			val &= ~AC_RECORD;
+		}
+
+		AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int nuc900_ac97_probe(struct platform_device *pdev,
+					struct snd_soc_dai *dai)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+	unsigned long val;
+
+	mutex_lock(&ac97_mutex);
+
+	/* enable unit clock */
+	clk_enable(nuc900_audio->clk);
+
+	/* enable audio controller and AC-link interface */
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
+	val |= (IIS_AC_PIN_SEL | ACLINK_EN);
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
+
+	mutex_unlock(&ac97_mutex);
+
+	return 0;
+}
+
+static void nuc900_ac97_remove(struct platform_device *pdev,
+						struct snd_soc_dai *dai)
+{
+	struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
+
+	clk_disable(nuc900_audio->clk);
+}
+
+static struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
+	.trigger	= nuc900_ac97_trigger,
+};
+
+struct snd_soc_dai nuc900_ac97_dai = {
+	.name			= "nuc900-ac97",
+	.probe			= nuc900_ac97_probe,
+	.remove			= nuc900_ac97_remove,
+	.ac97_control		= 1,
+	.playback = {
+		.rates		= SNDRV_PCM_RATE_8000_48000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min	= 1,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= SNDRV_PCM_RATE_8000_48000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min	= 1,
+		.channels_max	= 2,
+	},
+	.ops = &nuc900_ac97_dai_ops,
+}
+EXPORT_SYMBOL_GPL(nuc900_ac97_dai);
+
+static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev)
+{
+	struct nuc900_audio *nuc900_audio;
+	int ret;
+
+	if (nuc900_ac97_data)
+		return -EBUSY;
+
+	nuc900_audio = kzalloc(sizeof(struct nuc900_audio), GFP_KERNEL);
+	if (!nuc900_audio)
+		return -ENOMEM;
+
+	spin_lock_init(&nuc900_audio->lock);
+
+	nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!nuc900_audio->res) {
+		ret = -ENODEV;
+		goto out0;
+	}
+
+	if (!request_mem_region(nuc900_audio->res->start,
+			resource_size(nuc900_audio->res), pdev->name)) {
+		ret = -EBUSY;
+		goto out0;
+	}
+
+	nuc900_audio->mmio = ioremap(nuc900_audio->res->start,
+					resource_size(nuc900_audio->res));
+	if (!nuc900_audio->mmio) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	nuc900_audio->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(nuc900_audio->clk)) {
+		ret = PTR_ERR(nuc900_audio->clk);
+		goto out2;
+	}
+
+	nuc900_audio->irq_num = platform_get_irq(pdev, 0);
+	if (!nuc900_audio->irq_num) {
+		ret = -EBUSY;
+		goto out2;
+	}
+
+	nuc900_ac97_data = nuc900_audio;
+
+	nuc900_audio->dev = nuc900_ac97_dai.dev =  &pdev->dev;
+
+	ret = snd_soc_register_dai(&nuc900_ac97_dai);
+	if (ret)
+		goto out3;
+
+	mfp_set_groupg(nuc900_audio->dev); /* enbale ac97 multifunction pin*/
+
+	return 0;
+
+out3:
+	clk_put(nuc900_audio->clk);
+out2:
+	iounmap(nuc900_audio->mmio);
+out1:
+	release_mem_region(nuc900_audio->res->start,
+					resource_size(nuc900_audio->res));
+out0:
+	kfree(nuc900_audio);
+	return ret;
+}
+
+static int __devexit nuc900_ac97_drvremove(struct platform_device *pdev)
+{
+
+	snd_soc_unregister_dai(&nuc900_ac97_dai);
+
+	clk_put(nuc900_ac97_data->clk);
+	iounmap(nuc900_ac97_data->mmio);
+	release_mem_region(nuc900_ac97_data->res->start,
+				resource_size(nuc900_ac97_data->res));
+
+	nuc900_ac97_data = NULL;
+
+	return 0;
+}
+
+static struct platform_driver nuc900_ac97_driver = {
+	.driver	= {
+		.name	= "nuc900-audio",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= nuc900_ac97_drvprobe,
+	.remove		= __devexit_p(nuc900_ac97_drvremove),
+};
+
+static int __init nuc900_ac97_init(void)
+{
+	return platform_driver_register(&nuc900_ac97_driver);
+}
+
+static void __exit nuc900_ac97_exit(void)
+{
+	platform_driver_unregister(&nuc900_ac97_driver);
+}
+
+module_init(nuc900_ac97_init);
+module_exit(nuc900_ac97_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("NUC900 AC97 SoC driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-ac97");
diff --git a/sound/soc/nuc900/nuc900-audio.c b/sound/soc/nuc900/nuc900-audio.c
new file mode 100644
index 0000000..72e6f51
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-audio.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2010 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@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;version 2 of the License.
+ *
+ */
+
+#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 "../codecs/ac97.h"
+#include "nuc900-audio.h"
+
+static struct snd_soc_dai_link nuc900evb_ac97_dai = {
+	.name		= "AC97",
+	.stream_name	= "AC97 HiFi",
+	.cpu_dai	= &nuc900_ac97_dai,
+	.codec_dai	= &ac97_dai,
+};
+
+static struct snd_soc_card nuc900evb_audio_machine = {
+	.name		= "NUC900EVB_AC97",
+	.dai_link	= &nuc900evb_ac97_dai,
+	.num_links	= 1,
+	.platform	= &nuc900_soc_platform,
+};
+
+static struct snd_soc_device nuc900evb_ac97_devdata = {
+	.card		= &nuc900evb_audio_machine,
+	.codec_dev	= &soc_codec_dev_ac97,
+};
+
+static struct platform_device *nuc900evb_asoc_dev;
+
+static int __init nuc900evb_audio_init(void)
+{
+	int ret;
+
+	ret = -ENOMEM;
+	nuc900evb_asoc_dev = platform_device_alloc("soc-audio", -1);
+	if (!nuc900evb_asoc_dev)
+		goto out;
+
+	/* nuc900 board audio device */
+	platform_set_drvdata(nuc900evb_asoc_dev, &nuc900evb_ac97_devdata);
+
+	nuc900evb_ac97_devdata.dev = &nuc900evb_asoc_dev->dev;
+	ret = platform_device_add(nuc900evb_asoc_dev);
+
+	if (ret) {
+		platform_device_put(nuc900evb_asoc_dev);
+		nuc900evb_asoc_dev = NULL;
+	}
+
+out:
+	return ret;
+}
+
+static void __exit nuc900evb_audio_exit(void)
+{
+	platform_device_unregister(nuc900evb_asoc_dev);
+}
+
+module_init(nuc900evb_audio_init);
+module_exit(nuc900evb_audio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NUC900 Series ASoC audio support");
+MODULE_AUTHOR("Wan ZongShun");
diff --git a/sound/soc/nuc900/nuc900-audio.h b/sound/soc/nuc900/nuc900-audio.h
new file mode 100644
index 0000000..3038f51
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-audio.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2010 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@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;version 2 of the License.
+ *
+ */
+
+#ifndef _NUC900_AUDIO_H
+#define _NUC900_AUDIO_H
+
+#include <linux/io.h>
+
+/* Audio Control Registers */
+#define ACTL_CON		0x00
+#define ACTL_RESET		0x04
+#define ACTL_RDSTB		0x08
+#define ACTL_RDST_LENGTH	0x0C
+#define ACTL_RDSTC		0x10
+#define ACTL_RSR		0x14
+#define ACTL_PDSTB		0x18
+#define ACTL_PDST_LENGTH	0x1C
+#define ACTL_PDSTC		0x20
+#define ACTL_PSR		0x24
+#define ACTL_IISCON		0x28
+#define ACTL_ACCON		0x2C
+#define ACTL_ACOS0		0x30
+#define ACTL_ACOS1		0x34
+#define ACTL_ACOS2		0x38
+#define ACTL_ACIS0		0x3C
+#define ACTL_ACIS1		0x40
+#define ACTL_ACIS2		0x44
+#define ACTL_COUNTER		0x48
+
+/* bit definition of REG_ACTL_CON register */
+#define R_DMA_IRQ		0x1000
+#define T_DMA_IRQ		0x0800
+#define IIS_AC_PIN_SEL		0x0100
+#define FIFO_TH			0x0080
+#define ADC_EN			0x0010
+#define M80_EN			0x0008
+#define ACLINK_EN		0x0004
+#define IIS_EN			0x0002
+
+/* bit definition of REG_ACTL_RESET register */
+#define W5691_PLAY		0x20000
+#define ACTL_RESET_BIT		0x10000
+#define RECORD_RIGHT_CHNNEL	0x08000
+#define RECORD_LEFT_CHNNEL	0x04000
+#define PLAY_RIGHT_CHNNEL	0x02000
+#define PLAY_LEFT_CHNNEL	0x01000
+#define DAC_PLAY		0x00800
+#define ADC_RECORD		0x00400
+#define M80_PLAY		0x00200
+#define AC_RECORD		0x00100
+#define AC_PLAY			0x00080
+#define IIS_RECORD		0x00040
+#define IIS_PLAY		0x00020
+#define DAC_RESET		0x00010
+#define ADC_RESET		0x00008
+#define M80_RESET		0x00004
+#define AC_RESET		0x00002
+#define IIS_RESET		0x00001
+
+/* bit definition of REG_ACTL_ACCON register */
+#define AC_BCLK_PU_EN		0x20
+#define AC_R_FINISH		0x10
+#define AC_W_FINISH		0x08
+#define AC_W_RES		0x04
+#define AC_C_RES		0x02
+
+/* bit definition of ACTL_RSR register */
+#define R_FIFO_EMPTY		0x04
+#define R_DMA_END_IRQ		0x02
+#define R_DMA_MIDDLE_IRQ	0x01
+
+/* bit definition of ACTL_PSR register */
+#define P_FIFO_EMPTY		0x04
+#define P_DMA_END_IRQ		0x02
+#define P_DMA_MIDDLE_IRQ	0x01
+
+/* bit definition of ACTL_ACOS0 register */
+#define SLOT1_VALID		0x01
+#define SLOT2_VALID		0x02
+#define SLOT3_VALID		0x04
+#define SLOT4_VALID		0x08
+#define VALID_FRAME		0x10
+
+/* bit definition of ACTL_ACOS1 register */
+#define R_WB			0x80
+
+#define CODEC_READY		0x10
+#define RESET_PRSR		0x00
+#define AUDIO_WRITE(addr, val)	__raw_writel(val, addr)
+#define AUDIO_READ(addr)	__raw_readl(addr)
+
+struct nuc900_audio {
+	void __iomem *mmio;
+	spinlock_t lock;
+	dma_addr_t dma_addr[2];
+	unsigned long buffersize[2];
+	unsigned long irq_num;
+	struct snd_pcm_substream *substream;
+	struct resource *res;
+	struct clk *clk;
+	struct device *dev;
+
+};
+
+extern struct nuc900_audio *nuc900_ac97_data;
+extern struct snd_soc_dai nuc900_ac97_dai;
+extern struct snd_soc_platform nuc900_soc_platform;
+
+#endif /*end _NUC900_AUDIO_H */
diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c
new file mode 100644
index 0000000..e81e803
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-pcm.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2010 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@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;version 2 of the License.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.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 <mach/hardware.h>
+
+#include "nuc900-audio.h"
+
+static const struct snd_pcm_hardware nuc900_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_PAUSE |
+					SNDRV_PCM_INFO_RESUME,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= 4*1024,
+	.period_bytes_min	= 1*1024,
+	.period_bytes_max	= 4*1024,
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio = runtime->private_data;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&nuc900_audio->lock, flags);
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	nuc900_audio->substream = substream;
+	nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr;
+	nuc900_audio->buffersize[substream->stream] =
+						params_buffer_bytes(params);
+
+	spin_unlock_irqrestore(&nuc900_audio->lock, flags);
+
+	return ret;
+}
+
+static void nuc900_update_dma_register(struct snd_pcm_substream *substream,
+				dma_addr_t dma_addr, size_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio = runtime->private_data;
+	void __iomem *mmio_addr, *mmio_len;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
+		mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
+	} else {
+		mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
+		mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
+	}
+
+	AUDIO_WRITE(mmio_addr, dma_addr);
+	AUDIO_WRITE(mmio_len, count);
+}
+
+static void nuc900_dma_start(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio = runtime->private_data;
+	unsigned long val;
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
+	val |= (T_DMA_IRQ | R_DMA_IRQ);
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
+}
+
+static void nuc900_dma_stop(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio = runtime->private_data;
+	unsigned long val;
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
+	val &= ~(T_DMA_IRQ | R_DMA_IRQ);
+	AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
+}
+
+static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
+{
+	struct snd_pcm_substream *substream = dev_id;
+	struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
+	unsigned long val;
+
+	spin_lock(&nuc900_audio->lock);
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
+
+	if (val & R_DMA_IRQ) {
+		AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
+
+		val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
+
+		if (val & R_DMA_MIDDLE_IRQ) {
+			val |= R_DMA_MIDDLE_IRQ;
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
+		}
+
+		if (val & R_DMA_END_IRQ) {
+			val |= R_DMA_END_IRQ;
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
+		}
+	} else if (val & T_DMA_IRQ) {
+		AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
+
+		val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
+
+		if (val & P_DMA_MIDDLE_IRQ) {
+			val |= P_DMA_MIDDLE_IRQ;
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
+		}
+
+		if (val & P_DMA_END_IRQ) {
+			val |= P_DMA_END_IRQ;
+			AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
+		}
+	} else {
+		dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
+		spin_unlock(&nuc900_audio->lock);
+		return IRQ_HANDLED;
+	}
+
+	spin_unlock(&nuc900_audio->lock);
+
+	snd_pcm_period_elapsed(substream);
+
+	return IRQ_HANDLED;
+}
+
+static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio = runtime->private_data;
+	unsigned long flags, val;
+
+	spin_lock_irqsave(&nuc900_audio->lock, flags);
+
+	nuc900_update_dma_register(substream,
+				nuc900_audio->dma_addr[substream->stream],
+				nuc900_audio->buffersize[substream->stream]);
+
+	val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
+
+	switch (runtime->channels) {
+	case 1:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
+			val |= PLAY_RIGHT_CHNNEL;
+		} else {
+			val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
+			val |= RECORD_RIGHT_CHNNEL;
+		}
+		AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+		break;
+	case 2:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
+		else
+			val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
+		AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
+		break;
+	default:
+		return -EINVAL;
+	}
+	spin_unlock_irqrestore(&nuc900_audio->lock, flags);
+	return 0;
+}
+
+static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		nuc900_dma_start(substream);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		nuc900_dma_stop(substream);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+int nuc900_dma_getposition(struct snd_pcm_substream *substream,
+					dma_addr_t *src, dma_addr_t *dst)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio = runtime->private_data;
+
+	if (src != NULL)
+		*src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
+
+	if (dst != NULL)
+		*dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
+
+	return 0;
+}
+
+static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	dma_addr_t src, dst;
+	unsigned long res;
+
+	nuc900_dma_getposition(substream, &src, &dst);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		res = dst - runtime->dma_addr;
+	else
+		res = src - runtime->dma_addr;
+
+	return bytes_to_frames(substream->runtime, res);
+}
+
+static int nuc900_dma_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio;
+
+	snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
+
+	nuc900_audio = nuc900_ac97_data;
+
+	if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
+			IRQF_DISABLED, "nuc900-dma", substream))
+		return -EBUSY;
+
+	runtime->private_data = nuc900_audio;
+
+	return 0;
+}
+
+static int nuc900_dma_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct nuc900_audio *nuc900_audio = runtime->private_data;
+
+	free_irq(nuc900_audio->irq_num, substream);
+
+	return 0;
+}
+
+static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
+	struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+					runtime->dma_area,
+					runtime->dma_addr,
+					runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops nuc900_dma_ops = {
+	.open		= nuc900_dma_open,
+	.close		= nuc900_dma_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= nuc900_dma_hw_params,
+	.hw_free	= nuc900_dma_hw_free,
+	.prepare	= nuc900_dma_prepare,
+	.trigger	= nuc900_dma_trigger,
+	.pointer	= nuc900_dma_pointer,
+	.mmap		= nuc900_dma_mmap,
+};
+
+static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
+static int nuc900_dma_new(struct snd_card *card,
+	struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &nuc900_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+		card->dev, 4 * 1024, (4 * 1024) - 1);
+
+	return 0;
+}
+
+struct snd_soc_platform nuc900_soc_platform = {
+	.name		= "nuc900-dma",
+	.pcm_ops	= &nuc900_dma_ops,
+	.pcm_new	= nuc900_dma_new,
+	.pcm_free	= nuc900_dma_free_dma_buffers,
+}
+EXPORT_SYMBOL_GPL(nuc900_soc_platform);
+
+static int __init nuc900_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&nuc900_soc_platform);
+}
+
+static void __exit nuc900_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&nuc900_soc_platform);
+}
+
+module_init(nuc900_soc_platform_init);
+module_exit(nuc900_soc_platform_exit);
+
+MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("nuc900 Audio DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 6f44cb4..86f21390 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -59,6 +59,7 @@
 	int				configured;
 	unsigned int			in_freq;
 	int				clk_div;
+	int				wlen;
 };
 
 #define to_mcbsp(priv)	container_of((priv), struct omap_mcbsp_data, bus_id)
@@ -154,20 +155,51 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
+	struct omap_pcm_dma_data *dma_data;
 	int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id);
-	int samples;
+	int words;
+
+	dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
 
 	/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
 	if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
-		samples = snd_pcm_lib_period_bytes(substream) >> 1;
+		/*
+		 * Configure McBSP threshold based on either:
+		 * packet_size, when the sDMA is in packet mode, or
+		 * based on the period size.
+		 */
+		if (dma_data->packet_size)
+			words = dma_data->packet_size;
+		else
+			words = snd_pcm_lib_period_bytes(substream) /
+							(mcbsp_data->wlen / 8);
 	else
-		samples = 1;
+		words = 1;
 
 	/* Configure McBSP internal buffer usage */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, samples - 1);
+		omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, words);
 	else
-		omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, samples - 1);
+		omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, words);
+}
+
+static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params,
+				    struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *buffer_size = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct omap_mcbsp_data *mcbsp_data = rule->private;
+	struct snd_interval frames;
+	int size;
+
+	snd_interval_any(&frames);
+	size = omap_mcbsp_get_fifo_size(mcbsp_data->bus_id);
+
+	frames.min = size / channels->min;
+	frames.integer = 1;
+	return snd_interval_refine(buffer_size, &frames);
 }
 
 static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
@@ -182,33 +214,35 @@
 	if (!cpu_dai->active)
 		err = omap_mcbsp_request(bus_id);
 
+	/*
+	 * OMAP3 McBSP FIFO is word structured.
+	 * McBSP2 has 1024 + 256 = 1280 word long buffer,
+	 * McBSP1,3,4,5 has 128 word long buffer
+	 * This means that the size of the FIFO depends on the sample format.
+	 * For example on McBSP3:
+	 * 16bit samples: size is 128 * 2 = 256 bytes
+	 * 32bit samples: size is 128 * 4 = 512 bytes
+	 * It is simpler to place constraint for buffer and period based on
+	 * channels.
+	 * McBSP3 as example again (16 or 32 bit samples):
+	 * 1 channel (mono): size is 128 frames (128 words)
+	 * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
+	 * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
+	 */
 	if (cpu_is_omap343x()) {
-		int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id);
-		int max_period;
-
 		/*
-		 * McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer.
-		 * Set constraint for minimum buffer size to the same than FIFO
-		 * size in order to avoid underruns in playback startup because
-		 * HW is keeping the DMA request active until FIFO is filled.
-		 */
-		if (bus_id == 1)
-			snd_pcm_hw_constraint_minmax(substream->runtime,
-					SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-					4096, UINT_MAX);
+		* Rule for the buffer size. We should not allow
+		* smaller buffer than the FIFO size to avoid underruns
+		*/
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_CHANNELS,
+				    omap_mcbsp_hwrule_min_buffersize,
+				    mcbsp_data,
+				    SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
 
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			max_period = omap_mcbsp_get_max_tx_threshold(bus_id);
-		else
-			max_period = omap_mcbsp_get_max_rx_threshold(bus_id);
-
-		max_period++;
-		max_period <<= 1;
-
-		if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
-			snd_pcm_hw_constraint_minmax(substream->runtime,
-						SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-						32, max_period);
+		/* Make sure, that the period size is always even */
+		snd_pcm_hw_constraint_step(substream->runtime, 0,
+					   SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
 	}
 
 	return err;
@@ -289,11 +323,14 @@
 	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;
+	struct omap_pcm_dma_data *dma_data;
+	int dma, bus_id = mcbsp_data->bus_id;
 	int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
+	int pkt_size = 0;
 	unsigned long port;
 	unsigned int format, div, framesize, master;
 
+	dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream];
 	if (cpu_class_is_omap1()) {
 		dma = omap1_dma_reqs[bus_id][substream->stream];
 		port = omap1_mcbsp_port[bus_id][substream->stream];
@@ -306,35 +343,74 @@
 	} else if (cpu_is_omap343x()) {
 		dma = omap24xx_dma_reqs[bus_id][substream->stream];
 		port = omap34xx_mcbsp_port[bus_id][substream->stream];
-		omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold =
-						omap_mcbsp_set_threshold;
-		/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
-		if (omap_mcbsp_get_dma_op_mode(bus_id) ==
-						MCBSP_DMA_MODE_THRESHOLD)
-			sync_mode = OMAP_DMA_SYNC_FRAME;
 	} else {
 		return -ENODEV;
 	}
-	omap_mcbsp_dai_dma_params[id][substream->stream].name =
-		substream->stream ? "Audio Capture" : "Audio Playback";
-	omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
-	omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
-	omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode;
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
-		omap_mcbsp_dai_dma_params[id][substream->stream].data_type =
-			 OMAP_DMA_DATA_TYPE_S16;
+		dma_data->data_type = OMAP_DMA_DATA_TYPE_S16;
+		wlen = 16;
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
-		omap_mcbsp_dai_dma_params[id][substream->stream].data_type =
-			 OMAP_DMA_DATA_TYPE_S32;
+		dma_data->data_type = OMAP_DMA_DATA_TYPE_S32;
+		wlen = 32;
 		break;
 	default:
 		return -EINVAL;
 	}
+	if (cpu_is_omap343x()) {
+		dma_data->set_threshold = omap_mcbsp_set_threshold;
+		/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
+		if (omap_mcbsp_get_dma_op_mode(bus_id) ==
+						MCBSP_DMA_MODE_THRESHOLD) {
+			int period_words, max_thrsh;
 
-	snd_soc_dai_set_dma_data(cpu_dai, substream,
-		&omap_mcbsp_dai_dma_params[id][substream->stream]);
+			period_words = params_period_bytes(params) / (wlen / 8);
+			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+				max_thrsh = omap_mcbsp_get_max_tx_threshold(
+							    mcbsp_data->bus_id);
+			else
+				max_thrsh = omap_mcbsp_get_max_rx_threshold(
+							    mcbsp_data->bus_id);
+			/*
+			 * If the period contains less or equal number of words,
+			 * we are using the original threshold mode setup:
+			 * McBSP threshold = sDMA frame size = period_size
+			 * Otherwise we switch to sDMA packet mode:
+			 * McBSP threshold = sDMA packet size
+			 * sDMA frame size = period size
+			 */
+			if (period_words > max_thrsh) {
+				int divider = 0;
+
+				/*
+				 * Look for the biggest threshold value, which
+				 * divides the period size evenly.
+				 */
+				divider = period_words / max_thrsh;
+				if (period_words % max_thrsh)
+					divider++;
+				while (period_words % divider &&
+					divider < period_words)
+					divider++;
+				if (divider == period_words)
+					return -EINVAL;
+
+				pkt_size = period_words / divider;
+				sync_mode = OMAP_DMA_SYNC_PACKET;
+			} else {
+				sync_mode = OMAP_DMA_SYNC_FRAME;
+			}
+		}
+	}
+
+	dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback";
+	dma_data->dma_req = dma;
+	dma_data->port_addr = port;
+	dma_data->sync_mode = sync_mode;
+	dma_data->packet_size = pkt_size;
+
+	snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
 
 	if (mcbsp_data->configured) {
 		/* McBSP already configured by another stream */
@@ -360,7 +436,6 @@
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
 		/* Set word lengths */
-		wlen = 16;
 		regs->rcr2	|= RWDLEN2(OMAP_MCBSP_WORD_16);
 		regs->rcr1	|= RWDLEN1(OMAP_MCBSP_WORD_16);
 		regs->xcr2	|= XWDLEN2(OMAP_MCBSP_WORD_16);
@@ -368,7 +443,6 @@
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
 		/* Set word lengths */
-		wlen = 32;
 		regs->rcr2	|= RWDLEN2(OMAP_MCBSP_WORD_32);
 		regs->rcr1	|= RWDLEN1(OMAP_MCBSP_WORD_32);
 		regs->xcr2	|= XWDLEN2(OMAP_MCBSP_WORD_32);
@@ -409,6 +483,7 @@
 	}
 
 	omap_mcbsp_config(bus_id, &mcbsp_data->regs);
+	mcbsp_data->wlen = wlen;
 	mcbsp_data->configured = 1;
 
 	return 0;
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index 87ce842..9eecac1 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -43,12 +43,14 @@
 
 static struct regulator *omap3pandora_dac_reg;
 
-static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params, unsigned int fmt)
+static int omap3pandora_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;
+	int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		  SND_SOC_DAIFMT_CBS_CFS;
 	int ret;
 
 	/* Set codec DAI configuration */
@@ -91,24 +93,6 @@
 	return 0;
 }
 
-static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
-{
-	return omap3pandora_cmn_hw_params(substream, params,
-					  SND_SOC_DAIFMT_I2S |
-					  SND_SOC_DAIFMT_IB_NF |
-					  SND_SOC_DAIFMT_CBS_CFS);
-}
-
-static int omap3pandora_in_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
-{
-	return omap3pandora_cmn_hw_params(substream, params,
-					  SND_SOC_DAIFMT_I2S |
-					  SND_SOC_DAIFMT_NB_NF |
-					  SND_SOC_DAIFMT_CBS_CFS);
-}
-
 static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *k, int event)
 {
@@ -231,12 +215,8 @@
 	return snd_soc_dapm_sync(codec);
 }
 
-static struct snd_soc_ops omap3pandora_out_ops = {
-	.hw_params = omap3pandora_out_hw_params,
-};
-
-static struct snd_soc_ops omap3pandora_in_ops = {
-	.hw_params = omap3pandora_in_hw_params,
+static struct snd_soc_ops omap3pandora_ops = {
+	.hw_params = omap3pandora_hw_params,
 };
 
 /* Digital audio interface glue - connects codec <--> CPU */
@@ -246,14 +226,14 @@
 		.stream_name = "HiFi Out",
 		.cpu_dai = &omap_mcbsp_dai[0],
 		.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
-		.ops = &omap3pandora_out_ops,
+		.ops = &omap3pandora_ops,
 		.init = omap3pandora_out_init,
 	}, {
 		.name = "TWL4030",
 		.stream_name = "Line/Mic In",
 		.cpu_dai = &omap_mcbsp_dai[1],
 		.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
-		.ops = &omap3pandora_in_ops,
+		.ops = &omap3pandora_ops,
 		.init = omap3pandora_in_init,
 	}
 };
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 47d831e..88052d2 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -27,6 +27,7 @@
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <sound/core.h>
+#include <sound/jack.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
@@ -37,14 +38,22 @@
 #include "omap-pcm.h"
 #include "../codecs/tlv320aic3x.h"
 
+#define RX51_TVOUT_SEL_GPIO		40
+#define RX51_JACK_DETECT_GPIO		177
 /*
  * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This
  * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c
  */
 #define RX51_SPEAKER_AMP_TWL_GPIO	(192 + 7)
 
+enum {
+	RX51_JACK_DISABLED,
+	RX51_JACK_TVOUT,		/* tv-out */
+};
+
 static int rx51_spk_func;
 static int rx51_dmic_func;
+static int rx51_jack_func;
 
 static void rx51_ext_control(struct snd_soc_codec *codec)
 {
@@ -57,6 +66,9 @@
 	else
 		snd_soc_dapm_disable_pin(codec, "DMic");
 
+	gpio_set_value(RX51_TVOUT_SEL_GPIO,
+		       rx51_jack_func == RX51_JACK_TVOUT);
+
 	snd_soc_dapm_sync(codec);
 }
 
@@ -162,6 +174,40 @@
 	return 1;
 }
 
+static int rx51_get_jack(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = rx51_jack_func;
+
+	return 0;
+}
+
+static int rx51_set_jack(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	if (rx51_jack_func == ucontrol->value.integer.value[0])
+		return 0;
+
+	rx51_jack_func = ucontrol->value.integer.value[0];
+	rx51_ext_control(codec);
+
+	return 1;
+}
+
+static struct snd_soc_jack rx51_av_jack;
+
+static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
+	{
+		.gpio = RX51_JACK_DETECT_GPIO,
+		.name = "avdet-gpio",
+		.report = SND_JACK_VIDEOOUT,
+		.invert = 1,
+		.debounce_time = 200,
+	},
+};
+
 static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
 	SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
 	SND_SOC_DAPM_MIC("DMic", NULL),
@@ -177,10 +223,12 @@
 
 static const char *spk_function[] = {"Off", "On"};
 static const char *input_function[] = {"ADC", "Digital Mic"};
+static const char *jack_function[] = {"Off", "TV-OUT"};
 
 static const struct soc_enum rx51_enum[] = {
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
 };
 
 static const struct snd_kcontrol_new aic34_rx51_controls[] = {
@@ -188,10 +236,13 @@
 		     rx51_get_spk, rx51_set_spk),
 	SOC_ENUM_EXT("Input Select",  rx51_enum[1],
 		     rx51_get_input, rx51_set_input),
+	SOC_ENUM_EXT("Jack Function", rx51_enum[2],
+		     rx51_get_jack, rx51_set_jack),
 };
 
 static int rx51_aic34_init(struct snd_soc_codec *codec)
 {
+	struct snd_soc_card *card = codec->socdev->card;
 	int err;
 
 	/* Set up NC codec pins */
@@ -214,7 +265,16 @@
 
 	snd_soc_dapm_sync(codec);
 
-	return 0;
+	/* AV jack detection */
+	err = snd_soc_jack_new(card, "AV Jack",
+			       SND_JACK_VIDEOOUT, &rx51_av_jack);
+	if (err)
+		return err;
+	err = snd_soc_jack_add_gpios(&rx51_av_jack,
+				     ARRAY_SIZE(rx51_av_jack_gpios),
+				     rx51_av_jack_gpios);
+
+	return err;
 }
 
 /* Digital audio interface glue - connects codec <--> CPU */
@@ -259,6 +319,11 @@
 	if (!machine_is_nokia_rx51())
 		return -ENODEV;
 
+	err = gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel");
+	if (err)
+		goto err_gpio_tvout_sel;
+	gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0);
+
 	rx51_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!rx51_snd_device) {
 		err = -ENOMEM;
@@ -277,13 +342,19 @@
 err2:
 	platform_device_put(rx51_snd_device);
 err1:
+	gpio_free(RX51_TVOUT_SEL_GPIO);
+err_gpio_tvout_sel:
 
 	return err;
 }
 
 static void __exit rx51_soc_exit(void)
 {
+	snd_soc_jack_free_gpios(&rx51_av_jack, ARRAY_SIZE(rx51_av_jack_gpios),
+				rx51_av_jack_gpios);
+
 	platform_device_unregister(rx51_snd_device);
+	gpio_free(RX51_TVOUT_SEL_GPIO);
 }
 
 module_init(rx51_soc_init);
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 2a7cc22..213963ac 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,6 +1,6 @@
 config SND_S3C24XX_SOC
 	tristate "SoC Audio for the Samsung S3CXXXX chips"
-	depends on ARCH_S3C2410 || ARCH_S3C64XX
+	depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210
 	select S3C64XX_DMA if ARCH_S3C64XX
 	help
 	  Say Y or M if you want to add support for codecs attached to
@@ -120,8 +120,14 @@
 
 config SND_SOC_SMDK_WM9713
 	tristate "SoC AC97 Audio support for SMDK with WM9713"
-	depends on SND_S3C24XX_SOC && MACH_SMDK6410
+	depends on SND_S3C24XX_SOC && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110)
 	select SND_SOC_WM9713
 	select SND_S3C_SOC_AC97
 	help
 	  Sat Y if you want to add support for SoC audio on the SMDK.
+
+config SND_S3C64XX_SOC_SMARTQ
+	tristate "SoC I2S Audio support for SmartQ board"
+	depends on SND_S3C24XX_SOC && MACH_SMARTQ
+	select SND_S3C64XX_SOC_I2S
+	select SND_SOC_WM8750
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 81d8dc50..50172c3 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -29,6 +29,7 @@
 snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
 snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
 snd-soc-smdk-wm9713-objs := smdk_wm9713.o
+snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -41,3 +42,4 @@
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
 obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
 obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o
+obj-$(CONFIG_SND_S3C64XX_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
index ecf4fd0..31f6d45 100644
--- a/sound/soc/s3c24xx/s3c-ac97.c
+++ b/sound/soc/s3c24xx/s3c-ac97.c
@@ -31,7 +31,6 @@
 #define AC_CMD_DATA(x) (x & 0xffff)
 
 struct s3c_ac97_info {
-	unsigned           state;
 	struct clk         *ac97_clk;
 	void __iomem	   *regs;
 	struct mutex       lock;
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
index 13311c8..64376b2 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.c
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -32,7 +32,8 @@
 
 #undef S3C_IIS_V2_SUPPORTED
 
-#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \
+	|| defined(CONFIG_CPU_S5PV210)
 #define S3C_IIS_V2_SUPPORTED
 #endif
 
diff --git a/sound/soc/s3c24xx/smartq_wm8987.c b/sound/soc/s3c24xx/smartq_wm8987.c
new file mode 100644
index 0000000..b480348
--- /dev/null
+++ b/sound/soc/s3c24xx/smartq_wm8987.c
@@ -0,0 +1,295 @@
+/* sound/soc/s3c24xx/smartq_wm8987.c
+ *
+ * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
+ *
+ * Based on smdk6410_wm8987.c
+ *     Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
+ *     Graeme Gregory - graeme.gregory@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/platform_device.h>
+#include <linux/gpio.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+
+#include "s3c-dma.h"
+#include "s3c64xx-i2s.h"
+
+#include "../codecs/wm8750.h"
+
+/*
+ * WM8987 is register compatible with WM8750, so using that as base driver.
+ */
+
+static struct snd_soc_card snd_soc_smartq;
+
+static int smartq_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_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct s3c_i2sv2_rate_calc div;
+	unsigned int clk = 0;
+	int ret;
+
+	s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params),
+				s3c_i2sv2_get_clock(cpu_dai));
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 32000:
+	case 48000:
+	case 96000:
+		clk = 12288000;
+		break;
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+		clk = 11289600;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	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 = 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 = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set MCLK division for sample rate */
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, div.fs_div);
+	if (ret < 0)
+		return ret;
+
+	/* set prescaler division for sample rate */
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_PRESCALER,
+				     div.clk_div - 1);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * SmartQ WM8987 HiFi DAI operations.
+ */
+static struct snd_soc_ops smartq_hifi_ops = {
+	.hw_params = smartq_hifi_hw_params,
+};
+
+static struct snd_soc_jack smartq_jack;
+
+static struct snd_soc_jack_pin smartq_jack_pins[] = {
+	/* Disable speaker when headphone is plugged in */
+	{
+		.pin	= "Internal Speaker",
+		.mask	= SND_JACK_HEADPHONE,
+	},
+};
+
+static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
+	{
+		.gpio		= S3C64XX_GPL(12),
+		.name		= "headphone detect",
+		.report		= SND_JACK_HEADPHONE,
+		.debounce_time	= 200,
+	},
+};
+
+static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Internal Speaker"),
+	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+	SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *k,
+				int event)
+{
+	gpio_set_value(S3C64XX_GPK(12), SND_SOC_DAPM_EVENT_OFF(event));
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_MIC("Internal Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"Headphone Jack", NULL, "LOUT2"},
+	{"Headphone Jack", NULL, "ROUT2"},
+
+	{"Internal Speaker", NULL, "LOUT2"},
+	{"Internal Speaker", NULL, "ROUT2"},
+
+	{"Mic Bias", NULL, "Internal Mic"},
+	{"LINPUT2", NULL, "Mic Bias"},
+};
+
+static int smartq_wm8987_init(struct snd_soc_codec *codec)
+{
+	int err = 0;
+
+	/* Add SmartQ specific widgets */
+	snd_soc_dapm_new_controls(codec, wm8987_dapm_widgets,
+				  ARRAY_SIZE(wm8987_dapm_widgets));
+
+	/* add SmartQ specific controls */
+	err = snd_soc_add_controls(codec, wm8987_smartq_controls,
+				   ARRAY_SIZE(wm8987_smartq_controls));
+
+	if (err < 0)
+		return err;
+
+	/* setup SmartQ specific audio path */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	/* set endpoints to not connected */
+	snd_soc_dapm_nc_pin(codec, "LINPUT1");
+	snd_soc_dapm_nc_pin(codec, "RINPUT1");
+	snd_soc_dapm_nc_pin(codec, "OUT3");
+	snd_soc_dapm_nc_pin(codec, "ROUT1");
+
+	/* set endpoints to default off mode */
+	snd_soc_dapm_enable_pin(codec, "Internal Speaker");
+	snd_soc_dapm_enable_pin(codec, "Internal Mic");
+	snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+	err = snd_soc_dapm_sync(codec);
+	if (err)
+		return err;
+
+	/* Headphone jack detection */
+	err = snd_soc_jack_new(&snd_soc_smartq, "Headphone Jack",
+			       SND_JACK_HEADPHONE, &smartq_jack);
+	if (err)
+		return err;
+
+	err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins),
+				    smartq_jack_pins);
+	if (err)
+		return err;
+
+	err = snd_soc_jack_add_gpios(&smartq_jack,
+				     ARRAY_SIZE(smartq_jack_gpios),
+				     smartq_jack_gpios);
+
+	return err;
+}
+
+static struct snd_soc_dai_link smartq_dai[] = {
+	{
+		.name		= "wm8987",
+		.stream_name	= "SmartQ Hi-Fi",
+		.cpu_dai	= &s3c64xx_i2s_dai[0],
+		.codec_dai	= &wm8750_dai,
+		.init		= smartq_wm8987_init,
+		.ops		= &smartq_hifi_ops,
+	},
+};
+
+static struct snd_soc_card snd_soc_smartq = {
+	.name = "SmartQ",
+	.platform = &s3c24xx_soc_platform,
+	.dai_link = smartq_dai,
+	.num_links = ARRAY_SIZE(smartq_dai),
+};
+
+static struct snd_soc_device smartq_snd_devdata = {
+	.card = &snd_soc_smartq,
+	.codec_dev = &soc_codec_dev_wm8750,
+};
+
+static struct platform_device *smartq_snd_device;
+
+static int __init smartq_init(void)
+{
+	int ret;
+
+	if (!machine_is_smartq7() && !machine_is_smartq5()) {
+		pr_info("Only SmartQ is supported by this ASoC driver\n");
+		return -ENODEV;
+	}
+
+	smartq_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!smartq_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(smartq_snd_device, &smartq_snd_devdata);
+	smartq_snd_devdata.dev = &smartq_snd_device->dev;
+
+	ret = platform_device_add(smartq_snd_device);
+	if (ret) {
+		platform_device_put(smartq_snd_device);
+		return ret;
+	}
+
+	/* Initialise GPIOs used by amplifiers */
+	ret = gpio_request(S3C64XX_GPK(12), "amplifiers shutdown");
+	if (ret) {
+		dev_err(&smartq_snd_device->dev, "Failed to register GPK12\n");
+		goto err_unregister_device;
+	}
+
+	/* Disable amplifiers */
+	ret = gpio_direction_output(S3C64XX_GPK(12), 1);
+	if (ret) {
+		dev_err(&smartq_snd_device->dev, "Failed to configure GPK12\n");
+		goto err_free_gpio_amp_shut;
+	}
+
+	return 0;
+
+err_free_gpio_amp_shut:
+	gpio_free(S3C64XX_GPK(12));
+err_unregister_device:
+	platform_device_unregister(smartq_snd_device);
+
+	return ret;
+}
+
+static void __exit smartq_exit(void)
+{
+	snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
+				smartq_jack_gpios);
+
+	platform_device_unregister(smartq_snd_device);
+}
+
+module_init(smartq_init);
+module_exit(smartq_exit);
+
+/* Module information */
+MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/smdk_wm9713.c b/sound/soc/s3c24xx/smdk_wm9713.c
index 24fd39f..5527b9e 100644
--- a/sound/soc/s3c24xx/smdk_wm9713.c
+++ b/sound/soc/s3c24xx/smdk_wm9713.c
@@ -25,6 +25,9 @@
  * Default CFG switch settings to use this driver:
  *
  *   SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off
+ *   SMDKC100: Set CFG6 1-3 On, CFG7 1   On
+ *   SMDKC110: Set CFGB10 1-2 Off, CFGB12 1-3 On
+ *   SMDKV210: Set CFGB10 1-2 Off, CFGB12 1-3 On
  */
 
 /*
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c
index 5b9ac17..59e3fa7 100644
--- a/sound/soc/s6000/s6000-i2s.c
+++ b/sound/soc/s6000/s6000-i2s.c
@@ -451,16 +451,15 @@
 		goto err_release_none;
 	}
 
-	region = request_mem_region(scbmem->start,
-				    scbmem->end - scbmem->start + 1,
-				    pdev->name);
+	region = request_mem_region(scbmem->start, resource_size(scbmem),
+								pdev->name);
 	if (!region) {
 		dev_err(&pdev->dev, "I2S SCB region already claimed\n");
 		ret = -EBUSY;
 		goto err_release_none;
 	}
 
-	mmio = ioremap(scbmem->start, scbmem->end - scbmem->start + 1);
+	mmio = ioremap(scbmem->start, resource_size(scbmem));
 	if (!mmio) {
 		dev_err(&pdev->dev, "can't ioremap SCB region\n");
 		ret = -ENOMEM;
@@ -474,9 +473,8 @@
 		goto err_release_map;
 	}
 
-	region = request_mem_region(sifmem->start,
-				    sifmem->end - sifmem->start + 1,
-				    pdev->name);
+	region = request_mem_region(sifmem->start, resource_size(sifmem),
+								pdev->name);
 	if (!region) {
 		dev_err(&pdev->dev, "I2S SIF region already claimed\n");
 		ret = -EBUSY;
@@ -490,8 +488,8 @@
 		goto err_release_sif;
 	}
 
-	region = request_mem_region(dma1->start, dma1->end - dma1->start + 1,
-				    pdev->name);
+	region = request_mem_region(dma1->start, resource_size(dma1),
+								pdev->name);
 	if (!region) {
 		dev_err(&pdev->dev, "I2S DMA region already claimed\n");
 		ret = -EBUSY;
@@ -500,9 +498,8 @@
 
 	dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 	if (dma2) {
-		region = request_mem_region(dma2->start,
-					    dma2->end - dma2->start + 1,
-					    pdev->name);
+		region = request_mem_region(dma2->start, resource_size(dma2),
+								pdev->name);
 		if (!region) {
 			dev_err(&pdev->dev,
 				"I2S DMA region already claimed\n");
@@ -561,15 +558,15 @@
 	kfree(dev);
 err_release_dma2:
 	if (dma2)
-		release_mem_region(dma2->start, dma2->end - dma2->start + 1);
+		release_mem_region(dma2->start, resource_size(dma2));
 err_release_dma1:
-	release_mem_region(dma1->start, dma1->end - dma1->start + 1);
+	release_mem_region(dma1->start, resource_size(dma1));
 err_release_sif:
-	release_mem_region(sifmem->start, (sifmem->end - sifmem->start) + 1);
+	release_mem_region(sifmem->start, resource_size(sifmem));
 err_release_map:
 	iounmap(mmio);
 err_release_scb:
-	release_mem_region(scbmem->start, (scbmem->end - scbmem->start) + 1);
+	release_mem_region(scbmem->start, resource_size(scbmem));
 err_release_none:
 	return ret;
 }
@@ -590,19 +587,18 @@
 	kfree(dev);
 
 	region = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-	release_mem_region(region->start, region->end - region->start + 1);
+	release_mem_region(region->start, resource_size(region));
 
 	region = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 	if (region)
-		release_mem_region(region->start,
-				   region->end - region->start + 1);
+		release_mem_region(region->start, resource_size(region));
 
 	region = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	release_mem_region(region->start, (region->end - region->start) + 1);
+	release_mem_region(region->start, resource_size(region));
 
 	iounmap(mmio);
 	region = platform_get_resource(pdev, IORESOURCE_IO, 0);
-	release_mem_region(region->start, (region->end - region->start) + 1);
+	release_mem_region(region->start, resource_size(region));
 }
 
 static struct platform_driver s6000_i2s_driver = {
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index a1d14bc..52d7e8e 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -48,7 +48,7 @@
 
 config SND_FSI_AK4642
 	bool "FSI-AK4642 sound support"
-	depends on SND_SOC_SH4_FSI
+	depends on SND_SOC_SH4_FSI && I2C_SH_MOBILE
 	select SND_SOC_AK4642
 	help
 	  This option enables generic sound support for the
@@ -56,7 +56,7 @@
 
 config SND_FSI_DA7210
 	bool "FSI-DA7210 sound support"
-	depends on SND_SOC_SH4_FSI
+	depends on SND_SOC_SH4_FSI && I2C_SH_MOBILE
 	select SND_SOC_DA7210
 	help
 	  This option enables generic sound support for the
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
index be01854..dad575a 100644
--- a/sound/soc/sh/fsi-ak4642.c
+++ b/sound/soc/sh/fsi-ak4642.c
@@ -9,16 +9,7 @@
  * for more details.
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/io.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
 #include <sound/sh_fsi.h>
 #include <../sound/soc/codecs/ak4642.h>
 
@@ -38,7 +29,7 @@
 static struct snd_soc_dai_link fsi_dai_link = {
 	.name		= "AK4642",
 	.stream_name	= "AK4642",
-	.cpu_dai	= &fsi_soc_dai[0], /* fsi */
+	.cpu_dai	= &fsi_soc_dai[FSI_PORT_A],
 	.codec_dai	= &ak4642_dai,
 	.init		= fsi_ak4642_dai_init,
 	.ops		= NULL,
@@ -62,7 +53,7 @@
 {
 	int ret = -ENOMEM;
 
-	fsi_snd_device = platform_device_alloc("soc-audio", -1);
+	fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A);
 	if (!fsi_snd_device)
 		goto out;
 
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
index 33b4d17..121bbb0 100644
--- a/sound/soc/sh/fsi-da7210.c
+++ b/sound/soc/sh/fsi-da7210.c
@@ -10,16 +10,7 @@
  *  option) any later version.
  */
 
-#include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/i2c.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/sh_fsi.h>
 #include "../codecs/da7210.h"
 
@@ -33,7 +24,7 @@
 static struct snd_soc_dai_link fsi_da7210_dai = {
 	.name		= "DA7210",
 	.stream_name	= "DA7210",
-	.cpu_dai	= &fsi_soc_dai[1], /* FSI B */
+	.cpu_dai	= &fsi_soc_dai[FSI_PORT_B],
 	.codec_dai	= &da7210_dai,
 	.init		= fsi_da7210_init,
 };
@@ -56,7 +47,7 @@
 {
 	int ret;
 
-	fsi_da7210_snd_device = platform_device_alloc("soc-audio", -1);
+	fsi_da7210_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B);
 	if (!fsi_da7210_snd_device)
 		return -ENOMEM;
 
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index ec4acac..58c6bec 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -12,21 +12,12 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/delay.h>
-#include <linux/list.h>
 #include <linux/pm_runtime.h>
 #include <linux/io.h>
 #include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
-#include <sound/pcm_params.h>
 #include <sound/sh_fsi.h>
-#include <asm/atomic.h>
 
 #define DO_FMT		0x0000
 #define DOFF_CTL	0x0004
@@ -39,9 +30,11 @@
 #define DIDT		0x0020
 #define DODT		0x0024
 #define MUTE_ST		0x0028
-#define REG_END		MUTE_ST
+#define OUT_SEL		0x0030
+#define REG_END		OUT_SEL
 
-
+#define A_MST_CTLR	0x0180
+#define B_MST_CTLR	0x01A0
 #define CPU_INT_ST	0x01F4
 #define CPU_IEMSK	0x01F8
 #define CPU_IMSK	0x01FC
@@ -52,18 +45,18 @@
 #define CLK_RST		0x0210
 #define SOFT_RST	0x0214
 #define FIFO_SZ		0x0218
-#define MREG_START	CPU_INT_ST
+#define MREG_START	A_MST_CTLR
 #define MREG_END	FIFO_SZ
 
 /* DO_FMT */
 /* DI_FMT */
-#define CR_FMT(param) ((param) << 4)
-# define CR_MONO	0x0
-# define CR_MONO_D	0x1
-# define CR_PCM		0x2
-# define CR_I2S		0x3
-# define CR_TDM		0x4
-# define CR_TDM_D	0x5
+#define CR_MONO		(0x0 << 4)
+#define CR_MONO_D	(0x1 << 4)
+#define CR_PCM		(0x2 << 4)
+#define CR_I2S		(0x3 << 4)
+#define CR_TDM		(0x4 << 4)
+#define CR_TDM_D	(0x5 << 4)
+#define CR_SPDIF	0x00100120
 
 /* DOFF_CTL */
 /* DIFF_CTL */
@@ -75,6 +68,14 @@
 #define ERR_UNDER	0x00000001
 #define ST_ERR		(ERR_OVER | ERR_UNDER)
 
+/* CKG1 */
+#define ACKMD_MASK	0x00007000
+#define BPFMD_MASK	0x00000700
+
+/* A/B MST_CTLR */
+#define BP	(1 << 4)	/* Fix the signal of Biphase output */
+#define SE	(1 << 0)	/* Fix the master clock */
+
 /* CLK_RST */
 #define B_CLK		0x00000010
 #define A_CLK		0x00000001
@@ -119,9 +120,13 @@
 	int period_len;
 	int buffer_len;
 	int periods;
+
+	u32 mst_ctrl;
 };
 
-struct fsi_regs {
+struct fsi_core {
+	int ver;
+
 	u32 int_st;
 	u32 iemsk;
 	u32 imsk;
@@ -132,7 +137,7 @@
 	int irq;
 	struct fsi_priv fsia;
 	struct fsi_priv fsib;
-	struct fsi_regs *regs;
+	struct fsi_core *core;
 	struct sh_fsi_platform_info *info;
 	spinlock_t lock;
 };
@@ -169,24 +174,30 @@
 
 static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
 {
-	if (reg > REG_END)
+	if (reg > REG_END) {
+		pr_err("fsi: register access err (%s)\n", __func__);
 		return;
+	}
 
 	__fsi_reg_write((u32)(fsi->base + reg), data);
 }
 
 static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
 {
-	if (reg > REG_END)
+	if (reg > REG_END) {
+		pr_err("fsi: register access err (%s)\n", __func__);
 		return 0;
+	}
 
 	return __fsi_reg_read((u32)(fsi->base + reg));
 }
 
 static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
 {
-	if (reg > REG_END)
+	if (reg > REG_END) {
+		pr_err("fsi: register access err (%s)\n", __func__);
 		return;
+	}
 
 	__fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
 }
@@ -196,8 +207,10 @@
 	unsigned long flags;
 
 	if ((reg < MREG_START) ||
-	    (reg > MREG_END))
+	    (reg > MREG_END)) {
+		pr_err("fsi: register access err (%s)\n", __func__);
 		return;
+	}
 
 	spin_lock_irqsave(&master->lock, flags);
 	__fsi_reg_write((u32)(master->base + reg), data);
@@ -210,8 +223,10 @@
 	unsigned long flags;
 
 	if ((reg < MREG_START) ||
-	    (reg > MREG_END))
+	    (reg > MREG_END)) {
+		pr_err("fsi: register access err (%s)\n", __func__);
 		return 0;
+	}
 
 	spin_lock_irqsave(&master->lock, flags);
 	ret = __fsi_reg_read((u32)(master->base + reg));
@@ -226,8 +241,10 @@
 	unsigned long flags;
 
 	if ((reg < MREG_START) ||
-	    (reg > MREG_END))
+	    (reg > MREG_END)) {
+		pr_err("fsi: register access err (%s)\n", __func__);
 		return;
+	}
 
 	spin_lock_irqsave(&master->lock, flags);
 	__fsi_reg_mask_set((u32)(master->base + reg), mask, data);
@@ -349,8 +366,8 @@
 	u32 data = fsi_port_ab_io_bit(fsi, is_play);
 	struct fsi_master *master = fsi_get_master(fsi);
 
-	fsi_master_mask_set(master, master->regs->imsk,  data, data);
-	fsi_master_mask_set(master, master->regs->iemsk, data, data);
+	fsi_master_mask_set(master, master->core->imsk,  data, data);
+	fsi_master_mask_set(master, master->core->iemsk, data, data);
 }
 
 static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
@@ -358,18 +375,18 @@
 	u32 data = fsi_port_ab_io_bit(fsi, is_play);
 	struct fsi_master *master = fsi_get_master(fsi);
 
-	fsi_master_mask_set(master, master->regs->imsk,  data, 0);
-	fsi_master_mask_set(master, master->regs->iemsk, data, 0);
+	fsi_master_mask_set(master, master->core->imsk,  data, 0);
+	fsi_master_mask_set(master, master->core->iemsk, data, 0);
 }
 
 static u32 fsi_irq_get_status(struct fsi_master *master)
 {
-	return fsi_master_read(master, master->regs->int_st);
+	return fsi_master_read(master, master->core->int_st);
 }
 
 static void fsi_irq_clear_all_status(struct fsi_master *master)
 {
-	fsi_master_write(master, master->regs->int_st, 0x0000000);
+	fsi_master_write(master, master->core->int_st, 0);
 }
 
 static void fsi_irq_clear_status(struct fsi_priv *fsi)
@@ -381,7 +398,30 @@
 	data |= fsi_port_ab_io_bit(fsi, 1);
 
 	/* clear interrupt factor */
-	fsi_master_mask_set(master, master->regs->int_st, data, 0);
+	fsi_master_mask_set(master, master->core->int_st, data, 0);
+}
+
+/************************************************************************
+
+
+		SPDIF master clock function
+
+These functions are used later FSI2
+************************************************************************/
+static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	u32 val = BP | SE;
+
+	if (master->core->ver < 2) {
+		pr_err("fsi: register access err (%s)\n", __func__);
+		return;
+	}
+
+	if (enable)
+		fsi_master_mask_set(master, fsi->mst_ctrl, val, val);
+	else
+		fsi_master_mask_set(master, fsi->mst_ctrl, val, 0);
 }
 
 /************************************************************************
@@ -662,8 +702,8 @@
 			   struct snd_soc_dai *dai)
 {
 	struct fsi_priv *fsi = fsi_get_priv(substream);
-	const char *msg;
 	u32 flags = fsi_get_info_flags(fsi);
+	struct fsi_master *master = fsi_get_master(fsi);
 	u32 fmt;
 	u32 reg;
 	u32 data;
@@ -700,36 +740,40 @@
 	fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags);
 	switch (fmt) {
 	case SH_FSI_FMT_MONO:
-		msg = "MONO";
-		data = CR_FMT(CR_MONO);
+		data = CR_MONO;
 		fsi->chan = 1;
 		break;
 	case SH_FSI_FMT_MONO_DELAY:
-		msg = "MONO Delay";
-		data = CR_FMT(CR_MONO_D);
+		data = CR_MONO_D;
 		fsi->chan = 1;
 		break;
 	case SH_FSI_FMT_PCM:
-		msg = "PCM";
-		data = CR_FMT(CR_PCM);
+		data = CR_PCM;
 		fsi->chan = 2;
 		break;
 	case SH_FSI_FMT_I2S:
-		msg = "I2S";
-		data = CR_FMT(CR_I2S);
+		data = CR_I2S;
 		fsi->chan = 2;
 		break;
 	case SH_FSI_FMT_TDM:
-		msg = "TDM";
 		fsi->chan = is_play ?
 			SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
-		data = CR_FMT(CR_TDM) | (fsi->chan - 1);
+		data = CR_TDM | (fsi->chan - 1);
 		break;
 	case SH_FSI_FMT_TDM_DELAY:
-		msg = "TDM Delay";
 		fsi->chan = is_play ?
 			SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
-		data = CR_FMT(CR_TDM_D) | (fsi->chan - 1);
+		data = CR_TDM_D | (fsi->chan - 1);
+		break;
+	case SH_FSI_FMT_SPDIF:
+		if (master->core->ver < 2) {
+			dev_err(dai->dev, "This FSI can not use SPDIF\n");
+			return -EINVAL;
+		}
+		data = CR_SPDIF;
+		fsi->chan = 2;
+		fsi_spdif_clk_ctrl(fsi, 1);
+		fsi_reg_mask_set(fsi, OUT_SEL, 0x0010, 0x0010);
 		break;
 	default:
 		dev_err(dai->dev, "unknown format.\n");
@@ -737,12 +781,6 @@
 	}
 	fsi_reg_write(fsi, reg, data);
 
-	/*
-	 * clear clk reset if master mode
-	 */
-	if (is_master)
-		fsi_clk_ctrl(fsi, 1);
-
 	/* irq clear */
 	fsi_irq_disable(fsi, is_play);
 	fsi_irq_clear_status(fsi);
@@ -789,10 +827,93 @@
 	return ret;
 }
 
+static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct fsi_priv *fsi = fsi_get_priv(substream);
+	struct fsi_master *master = fsi_get_master(fsi);
+	int (*set_rate)(int is_porta, int rate) = master->info->set_rate;
+	int fsi_ver = master->core->ver;
+	int is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	int ret;
+
+	/* if slave mode, set_rate is not needed */
+	if (!fsi_is_master_mode(fsi, is_play))
+		return 0;
+
+	/* it is error if no set_rate */
+	if (!set_rate)
+		return -EIO;
+
+	ret = set_rate(fsi_is_port_a(fsi), params_rate(params));
+	if (ret > 0) {
+		u32 data = 0;
+
+		switch (ret & SH_FSI_ACKMD_MASK) {
+		default:
+			/* FALL THROUGH */
+		case SH_FSI_ACKMD_512:
+			data |= (0x0 << 12);
+			break;
+		case SH_FSI_ACKMD_256:
+			data |= (0x1 << 12);
+			break;
+		case SH_FSI_ACKMD_128:
+			data |= (0x2 << 12);
+			break;
+		case SH_FSI_ACKMD_64:
+			data |= (0x3 << 12);
+			break;
+		case SH_FSI_ACKMD_32:
+			if (fsi_ver < 2)
+				dev_err(dai->dev, "unsupported ACKMD\n");
+			else
+				data |= (0x4 << 12);
+			break;
+		}
+
+		switch (ret & SH_FSI_BPFMD_MASK) {
+		default:
+			/* FALL THROUGH */
+		case SH_FSI_BPFMD_32:
+			data |= (0x0 << 8);
+			break;
+		case SH_FSI_BPFMD_64:
+			data |= (0x1 << 8);
+			break;
+		case SH_FSI_BPFMD_128:
+			data |= (0x2 << 8);
+			break;
+		case SH_FSI_BPFMD_256:
+			data |= (0x3 << 8);
+			break;
+		case SH_FSI_BPFMD_512:
+			data |= (0x4 << 8);
+			break;
+		case SH_FSI_BPFMD_16:
+			if (fsi_ver < 2)
+				dev_err(dai->dev, "unsupported ACKMD\n");
+			else
+				data |= (0x7 << 8);
+			break;
+		}
+
+		fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
+		udelay(10);
+		fsi_clk_ctrl(fsi, 1);
+		ret = 0;
+	}
+
+	return ret;
+
+}
+
 static struct snd_soc_dai_ops fsi_dai_ops = {
 	.startup	= fsi_dai_startup,
 	.shutdown	= fsi_dai_shutdown,
 	.trigger	= fsi_dai_trigger,
+	.hw_params	= fsi_dai_hw_params,
 };
 
 /************************************************************************
@@ -965,11 +1086,6 @@
 	unsigned int irq;
 	int ret;
 
-	if (0 != pdev->id) {
-		dev_err(&pdev->dev, "current fsi support id 0 only now\n");
-		return -ENODEV;
-	}
-
 	id_entry = pdev->id_entry;
 	if (!id_entry) {
 		dev_err(&pdev->dev, "unknown fsi device\n");
@@ -998,14 +1114,21 @@
 		goto exit_kfree;
 	}
 
+	/* master setting */
 	master->irq		= irq;
 	master->info		= pdev->dev.platform_data;
+	master->core		= (struct fsi_core *)id_entry->driver_data;
+	spin_lock_init(&master->lock);
+
+	/* FSI A setting */
 	master->fsia.base	= master->base;
 	master->fsia.master	= master;
+	master->fsia.mst_ctrl	= A_MST_CTLR;
+
+	/* FSI B setting */
 	master->fsib.base	= master->base + 0x40;
 	master->fsib.master	= master;
-	master->regs		= (struct fsi_regs *)id_entry->driver_data;
-	spin_lock_init(&master->lock);
+	master->fsib.mst_ctrl	= B_MST_CTLR;
 
 	pm_runtime_enable(&pdev->dev);
 	pm_runtime_resume(&pdev->dev);
@@ -1085,21 +1208,27 @@
 	.runtime_resume		= fsi_runtime_nop,
 };
 
-static struct fsi_regs fsi_regs = {
+static struct fsi_core fsi1_core = {
+	.ver	= 1,
+
+	/* Interrupt */
 	.int_st	= INT_ST,
 	.iemsk	= IEMSK,
 	.imsk	= IMSK,
 };
 
-static struct fsi_regs fsi2_regs = {
+static struct fsi_core fsi2_core = {
+	.ver	= 2,
+
+	/* Interrupt */
 	.int_st	= CPU_INT_ST,
 	.iemsk	= CPU_IEMSK,
 	.imsk	= CPU_IMSK,
 };
 
 static struct platform_device_id fsi_id_table[] = {
-	{ "sh_fsi",	(kernel_ulong_t)&fsi_regs },
-	{ "sh_fsi2",	(kernel_ulong_t)&fsi2_regs },
+	{ "sh_fsi",	(kernel_ulong_t)&fsi1_core },
+	{ "sh_fsi2",	(kernel_ulong_t)&fsi2_core },
 };
 
 static struct platform_driver fsi_driver = {
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e048e09..844ae82 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -84,7 +84,7 @@
 /* codec register dump */
 static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
 {
-	int i, step = 1, count = 0;
+	int ret, i, step = 1, count = 0;
 
 	if (!codec->reg_cache_size)
 		return 0;
@@ -101,12 +101,24 @@
 		if (count >= PAGE_SIZE - 1)
 			break;
 
-		if (codec->display_register)
+		if (codec->display_register) {
 			count += codec->display_register(codec, buf + count,
 							 PAGE_SIZE - count, i);
-		else
-			count += snprintf(buf + count, PAGE_SIZE - count,
-					  "%4x", codec->read(codec, i));
+		} else {
+			/* If the read fails it's almost certainly due to
+			 * the register being volatile and the device being
+			 * powered off.
+			 */
+			ret = codec->read(codec, i);
+			if (ret >= 0)
+				count += snprintf(buf + count,
+						  PAGE_SIZE - count,
+						  "%4x", ret);
+			else
+				count += snprintf(buf + count,
+						  PAGE_SIZE - count,
+						  "<no data: %d>", ret);
+		}
 
 		if (count >= PAGE_SIZE - 1)
 			break;
@@ -2353,6 +2365,99 @@
 EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
 
 /**
+ * snd_soc_info_volsw_2r_sx - double with tlv and variable data size
+ *  mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int max = mc->max;
+	int min = mc->min;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = max-min;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx);
+
+/**
+ * snd_soc_get_volsw_2r_sx - double with tlv and variable data size
+ *  mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = (1<<mc->shift)-1;
+	int min = mc->min;
+	int val = snd_soc_read(codec, mc->reg) & mask;
+	int valr = snd_soc_read(codec, mc->rreg) & mask;
+
+	ucontrol->value.integer.value[0] = ((val & 0xff)-min) & mask;
+	ucontrol->value.integer.value[1] = ((valr & 0xff)-min) & mask;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx);
+
+/**
+ * snd_soc_put_volsw_2r_sx - double with tlv and variable data size
+ *  mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = (1<<mc->shift)-1;
+	int min = mc->min;
+	int ret;
+	unsigned int val, valr, oval, ovalr;
+
+	val = ((ucontrol->value.integer.value[0]+min) & 0xff);
+	val &= mask;
+	valr = ((ucontrol->value.integer.value[1]+min) & 0xff);
+	valr &= mask;
+
+	oval = snd_soc_read(codec, mc->reg) & mask;
+	ovalr = snd_soc_read(codec, mc->rreg) & mask;
+
+	ret = 0;
+	if (oval != val) {
+		ret = snd_soc_write(codec, mc->reg, val);
+		if (ret < 0)
+			return ret;
+	}
+	if (ovalr != valr) {
+		ret = snd_soc_write(codec, mc->rreg, valr);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
+
+/**
  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
  * @dai: DAI
  * @clk_id: DAI specific clock ID
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 7c2d677..cb61317 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -576,8 +576,6 @@
 	struct sound_unit *s;
 	const struct file_operations *new_fops = NULL;
 
-	lock_kernel ();
-
 	chain=unit&0x0F;
 	if(chain==4 || chain==5)	/* dsp/audio/dsp16 */
 	{
@@ -630,18 +628,19 @@
 		const struct file_operations *old_fops = file->f_op;
 		file->f_op = new_fops;
 		spin_unlock(&sound_loader_lock);
-		if(file->f_op->open)
+
+		if (file->f_op->open)
 			err = file->f_op->open(inode,file);
+
 		if (err) {
 			fops_put(file->f_op);
 			file->f_op = fops_get(old_fops);
 		}
+
 		fops_put(old_fops);
-		unlock_kernel();
 		return err;
 	}
 	spin_unlock(&sound_loader_lock);
-	unlock_kernel();
 	return -ENODEV;
 }
 
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 7a8ac1d..9feb00c 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -217,7 +217,7 @@
 
 	switch (protocol) {
 	case UAC_VERSION_1: {
-		struct uac_ac_header_descriptor_v1 *h1 = control_header;
+		struct uac1_ac_header_descriptor *h1 = control_header;
 
 		if (!h1->bInCollection) {
 			snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index b585511..b853f8d 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -19,33 +19,19 @@
 
 #include <linux/bitops.h>
 #include <linux/init.h>
-#include <linux/list.h>
-#include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/usb.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/info.h>
 #include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 
 #include "usbaudio.h"
 #include "card.h"
-#include "midi.h"
-#include "mixer.h"
-#include "proc.h"
-#include "quirks.h"
-#include "endpoint.h"
 #include "helper.h"
-#include "debug.h"
-#include "pcm.h"
-#include "urb.h"
-#include "format.h"
+#include "clock.h"
 
 static struct uac_clock_source_descriptor *
 	snd_usb_find_clock_source(struct usb_host_interface *ctrl_iface,
@@ -134,10 +120,7 @@
 	return !!data;
 }
 
-/* Try to find the clock source ID of a given clock entity */
-
 static int __uac_clock_find_source(struct snd_usb_audio *chip,
-				   struct usb_host_interface *host_iface,
 				   int entity_id, unsigned long *visited)
 {
 	struct uac_clock_source_descriptor *source;
@@ -154,11 +137,11 @@
 	}
 
 	/* first, see if the ID we're looking for is a clock source already */
-	source = snd_usb_find_clock_source(host_iface, entity_id);
+	source = snd_usb_find_clock_source(chip->ctrl_intf, entity_id);
 	if (source)
 		return source->bClockID;
 
-	selector = snd_usb_find_clock_selector(host_iface, entity_id);
+	selector = snd_usb_find_clock_selector(chip->ctrl_intf, entity_id);
 	if (selector) {
 		int ret;
 
@@ -168,6 +151,8 @@
 		if (ret < 0)
 			return ret;
 
+		/* Selector values are one-based */
+
 		if (ret > selector->bNrInPins || ret < 1) {
 			printk(KERN_ERR
 				"%s(): selector reported illegal value, id %d, ret %d\n",
@@ -176,27 +161,35 @@
 			return -EINVAL;
 		}
 
-		return __uac_clock_find_source(chip, host_iface,
-					       selector->baCSourceID[ret-1],
+		return __uac_clock_find_source(chip, selector->baCSourceID[ret-1],
 					       visited);
 	}
 
 	/* FIXME: multipliers only act as pass-thru element for now */
-	multiplier = snd_usb_find_clock_multiplier(host_iface, entity_id);
+	multiplier = snd_usb_find_clock_multiplier(chip->ctrl_intf, entity_id);
 	if (multiplier)
-		return __uac_clock_find_source(chip, host_iface,
-					       multiplier->bCSourceID, visited);
+		return __uac_clock_find_source(chip, multiplier->bCSourceID,
+						visited);
 
 	return -EINVAL;
 }
 
-int snd_usb_clock_find_source(struct snd_usb_audio *chip,
-			      struct usb_host_interface *host_iface,
-			      int entity_id)
+/*
+ * For all kinds of sample rate settings and other device queries,
+ * the clock source (end-leaf) must be used. However, clock selectors,
+ * clock multipliers and sample rate converters may be specified as
+ * clock source input to terminal. This functions walks the clock path
+ * to its end and tries to find the source.
+ *
+ * The 'visited' bitfield is used internally to detect recursive loops.
+ *
+ * Returns the clock source UnitID (>=0) on success, or an error.
+ */
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id)
 {
 	DECLARE_BITMAP(visited, 256);
 	memset(visited, 0, sizeof(visited));
-	return __uac_clock_find_source(chip, host_iface, entity_id, visited);
+	return __uac_clock_find_source(chip, entity_id, visited);
 }
 
 static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
@@ -211,11 +204,8 @@
 	ep = get_endpoint(alts, 0)->bEndpointAddress;
 
 	/* if endpoint doesn't have sampling rate control, bail out */
-	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
-		snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
-				   dev->devnum, iface, fmt->altsetting);
+	if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
 		return 0;
-	}
 
 	data[0] = rate;
 	data[1] = rate >> 8;
@@ -254,12 +244,13 @@
 	struct usb_device *dev = chip->dev;
 	unsigned char data[4];
 	int err, crate;
-	int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fmt->clock);
+	int clock = snd_usb_clock_find_source(chip, fmt->clock);
 
 	if (clock < 0)
 		return clock;
 
 	if (!uac_clock_source_is_valid(chip, clock)) {
+		/* TODO: should we try to find valid clock setups by ourself? */
 		snd_printk(KERN_ERR "%d:%d:%d: clock source %d is not valid, cannot use\n",
 			   dev->devnum, iface, fmt->altsetting, clock);
 		return -ENXIO;
diff --git a/sound/usb/clock.h b/sound/usb/clock.h
index beb2536..4663093 100644
--- a/sound/usb/clock.h
+++ b/sound/usb/clock.h
@@ -5,8 +5,6 @@
 			     struct usb_host_interface *alts,
 			     struct audioformat *fmt, int rate);
 
-int snd_usb_clock_find_source(struct snd_usb_audio *chip,
-			      struct usb_host_interface *host_iface,
-			      int entity_id);
+int snd_usb_clock_find_source(struct snd_usb_audio *chip, int entity_id);
 
 #endif /* __USBAUDIO_CLOCK_H */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 6f6596c..1a701f1 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -33,6 +33,7 @@
 #include "pcm.h"
 #include "helper.h"
 #include "format.h"
+#include "clock.h"
 
 /*
  * free a substream
@@ -275,7 +276,7 @@
 		/* get audio formats */
 		switch (protocol) {
 		case UAC_VERSION_1: {
-			struct uac_as_header_descriptor_v1 *as =
+			struct uac1_as_header_descriptor *as =
 				snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
 
 			if (!as) {
@@ -297,7 +298,7 @@
 		case UAC_VERSION_2: {
 			struct uac2_input_terminal_descriptor *input_term;
 			struct uac2_output_terminal_descriptor *output_term;
-			struct uac_as_header_descriptor_v2 *as =
+			struct uac2_as_header_descriptor *as =
 				snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
 
 			if (!as) {
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 30364ab..4387f54 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -264,13 +264,12 @@
  * on the audioformat table (audio class v2).
  */
 static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
-				       struct audioformat *fp,
-				       struct usb_host_interface *iface)
+				       struct audioformat *fp)
 {
 	struct usb_device *dev = chip->dev;
 	unsigned char tmp[2], *data;
 	int nr_triplets, data_size, ret = 0;
-	int clock = snd_usb_clock_find_source(chip, chip->ctrl_intf, fp->clock);
+	int clock = snd_usb_clock_find_source(chip, fp->clock);
 
 	if (clock < 0) {
 		snd_printk(KERN_ERR "%s(): unable to find clock source (clock %d)\n",
@@ -391,7 +390,7 @@
 		break;
 	case UAC_VERSION_2:
 		/* fp->channels is already set in this case */
-		ret = parse_audio_format_rates_v2(chip, fp, iface);
+		ret = parse_audio_format_rates_v2(chip, fp);
 		break;
 	}
 
@@ -450,7 +449,7 @@
 		framesize = le16_to_cpu(fmt->wSamplesPerFrame);
 		snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
 		fp->frame_size = framesize;
-		ret = parse_audio_format_rates_v2(chip, fp, iface);
+		ret = parse_audio_format_rates_v2(chip, fp);
 		break;
 	}
 	}
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 4678564..b9c2bc6 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -434,7 +434,7 @@
 			u8 cin = buffer[i] & 0x0f;
 			struct usbmidi_in_port *port = &ep->ports[cable];
 			int length;
-			
+
 			length = snd_usbmidi_cin_length[cin];
 			if (cin == 0xf && buffer[i + 1] >= 0xf8)
 				; /* realtime msg: no running status change */
@@ -628,13 +628,13 @@
 
 static struct usb_protocol_ops snd_usbmidi_midiman_ops = {
 	.input = snd_usbmidi_midiman_input,
-	.output = snd_usbmidi_standard_output, 
+	.output = snd_usbmidi_standard_output,
 	.output_packet = snd_usbmidi_output_midiman_packet,
 };
 
 static struct usb_protocol_ops snd_usbmidi_maudio_broken_running_status_ops = {
 	.input = snd_usbmidi_maudio_broken_running_status_input,
-	.output = snd_usbmidi_standard_output, 
+	.output = snd_usbmidi_standard_output,
 	.output_packet = snd_usbmidi_output_standard_packet,
 };
 
@@ -1248,7 +1248,7 @@
  */
 static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi* umidi,
 					   struct snd_usb_midi_endpoint_info* ep_info,
-			 		   struct snd_usb_midi_endpoint* rep)
+					   struct snd_usb_midi_endpoint* rep)
 {
 	struct snd_usb_midi_out_endpoint* ep;
 	unsigned int i;
@@ -1398,7 +1398,7 @@
 }
 
 static struct snd_rawmidi_substream *snd_usbmidi_find_substream(struct snd_usb_midi* umidi,
-							   int stream, int number)
+								int stream, int number)
 {
 	struct list_head* list;
 
@@ -1811,7 +1811,7 @@
 		snd_usbmidi_switch_roland_altsetting(umidi);
 
 	if (endpoint[0].out_ep || endpoint[0].in_ep)
-		return 0;	
+		return 0;
 
 	intf = umidi->iface;
 	if (!intf || intf->num_altsetting < 1)
@@ -1849,7 +1849,7 @@
 						 struct snd_usb_midi_endpoint_info* endpoints)
 {
 	int err, i;
-	
+
 	err = snd_usbmidi_detect_endpoints(umidi, endpoints, MIDI_MAX_ENDPOINTS);
 	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
 		if (endpoints[i].out_ep)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 736d134..c166db0 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -26,6 +26,22 @@
  *
  */
 
+/*
+ * TODOs, for both the mixer and the streaming interfaces:
+ *
+ *  - support for UAC2 effect units
+ *  - support for graphical equalizers
+ *  - RANGE and MEM set commands (UAC2)
+ *  - RANGE and MEM interrupt dispatchers (UAC2)
+ *  - audio channel clustering (UAC2)
+ *  - audio sample rate converter units (UAC2)
+ *  - proper handling of clock multipliers (UAC2)
+ *  - dispatch clock change notifications (UAC2)
+ *  	- stop PCM streams which use a clock that became invalid
+ *  	- stop PCM streams which use a clock selector that has changed
+ *  	- parse available sample rates again when clock sources changed
+ */
+
 #include <linux/bitops.h>
 #include <linux/init.h>
 #include <linux/list.h>
@@ -275,28 +291,28 @@
 
 static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
 {
+	struct snd_usb_audio *chip = cval->mixer->chip;
 	unsigned char buf[2];
 	int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
 	int timeout = 10;
 
 	while (timeout-- > 0) {
-		if (snd_usb_ctl_msg(cval->mixer->chip->dev,
-				    usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
-				    request,
+		if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
 				    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
-				    validx, cval->mixer->ctrlif | (cval->id << 8),
+				    validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
 				    buf, val_len, 100) >= val_len) {
 			*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
 			return 0;
 		}
 	}
 	snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
-		    request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
+		    request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
 	return -EINVAL;
 }
 
 static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
 {
+	struct snd_usb_audio *chip = cval->mixer->chip;
 	unsigned char buf[2 + 3*sizeof(__u16)]; /* enough space for one range */
 	unsigned char *val;
 	int ret, size;
@@ -312,16 +328,14 @@
 
 	memset(buf, 0, sizeof(buf));
 
-	ret = snd_usb_ctl_msg(cval->mixer->chip->dev,
-			      usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
-			      bRequest,
+	ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
 			      USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
-			      validx, cval->mixer->ctrlif | (cval->id << 8),
+			      validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
 			      buf, size, 1000);
 
 	if (ret < 0) {
 		snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
-			   request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
+			   request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type);
 		return ret;
 	}
 
@@ -397,6 +411,7 @@
 int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
 				int request, int validx, int value_set)
 {
+	struct snd_usb_audio *chip = cval->mixer->chip;
 	unsigned char buf[2];
 	int val_len, timeout = 10;
 
@@ -419,15 +434,14 @@
 	buf[0] = value_set & 0xff;
 	buf[1] = (value_set >> 8) & 0xff;
 	while (timeout-- > 0)
-		if (snd_usb_ctl_msg(cval->mixer->chip->dev,
-				    usb_sndctrlpipe(cval->mixer->chip->dev, 0),
-				    request,
+		if (snd_usb_ctl_msg(chip->dev,
+				    usb_sndctrlpipe(chip->dev, 0), request,
 				    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
-				    validx, cval->mixer->ctrlif | (cval->id << 8),
+				    validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
 				    buf, val_len, 100) >= 0)
 			return 0;
 	snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
-		    request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]);
+		    request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type, buf[0], buf[1]);
 	return -EINVAL;
 }
 
@@ -582,9 +596,9 @@
 		switch (iterm->type >> 16) {
 		case UAC_SELECTOR_UNIT:
 			strcpy(name, "Selector"); return 8;
-		case UAC_PROCESSING_UNIT_V1:
+		case UAC1_PROCESSING_UNIT:
 			strcpy(name, "Process Unit"); return 12;
-		case UAC_EXTENSION_UNIT_V1:
+		case UAC1_EXTENSION_UNIT:
 			strcpy(name, "Ext Unit"); return 8;
 		case UAC_MIXER_UNIT:
 			strcpy(name, "Mixer"); return 5;
@@ -672,8 +686,8 @@
 			term->name = uac_selector_unit_iSelector(d);
 			return 0;
 		}
-		case UAC_PROCESSING_UNIT_V1:
-		case UAC_EXTENSION_UNIT_V1: {
+		case UAC1_PROCESSING_UNIT:
+		case UAC1_EXTENSION_UNIT: {
 			struct uac_processing_unit_descriptor *d = p1;
 			if (d->bNrInPins) {
 				id = d->baSourceID[0];
@@ -745,6 +759,8 @@
  */
 static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
 {
+	struct snd_usb_audio *chip = cval->mixer->chip;
+
 	/* for failsafe */
 	cval->min = default_min;
 	cval->max = cval->min + 1;
@@ -767,7 +783,7 @@
 		if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
 		    get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
 			snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n",
-				   cval->id, cval->mixer->ctrlif, cval->control, cval->id);
+				   cval->id, snd_usb_ctrl_intf(chip), cval->control, cval->id);
 			return -EINVAL;
 		}
 		if (get_ctl_value(cval, UAC_GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) {
@@ -1199,14 +1215,6 @@
 		}
 	} else { /* UAC_VERSION_2 */
 		for (i = 0; i < 30/2; i++) {
-			/* From the USB Audio spec v2.0:
-			   bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
-			   each containing a set of bit pairs. If a Control is present,
-			   it must be Host readable. If a certain Control is not
-			   present then the bit pair must be set to 0b00.
-			   If a Control is present but read-only, the bit pair must be
-			   set to 0b01. If a Control is also Host programmable, the bit
-			   pair must be set to 0b11. The value 0b10 is not allowed. */
 			unsigned int ch_bits = 0;
 			unsigned int ch_read_only = 0;
 
@@ -1855,13 +1863,13 @@
 		return parse_audio_selector_unit(state, unitid, p1);
 	case UAC_FEATURE_UNIT:
 		return parse_audio_feature_unit(state, unitid, p1);
-	case UAC_PROCESSING_UNIT_V1:
+	case UAC1_PROCESSING_UNIT:
 	/*   UAC2_EFFECT_UNIT has the same value */
 		if (state->mixer->protocol == UAC_VERSION_1)
 			return parse_audio_processing_unit(state, unitid, p1);
 		else
 			return 0; /* FIXME - effect units not implemented yet */
-	case UAC_EXTENSION_UNIT_V1:
+	case UAC1_EXTENSION_UNIT:
 	/*   UAC2_PROCESSING_UNIT_V2 has the same value */
 		if (state->mixer->protocol == UAC_VERSION_1)
 			return parse_audio_extension_unit(state, unitid, p1);
@@ -1905,7 +1913,7 @@
 	struct usb_host_interface *hostif;
 	void *p;
 
-	hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
+	hostif = mixer->chip->ctrl_intf;
 	memset(&state, 0, sizeof(state));
 	state.chip = mixer->chip;
 	state.mixer = mixer;
@@ -1925,7 +1933,7 @@
 	p = NULL;
 	while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) {
 		if (mixer->protocol == UAC_VERSION_1) {
-			struct uac_output_terminal_descriptor_v1 *desc = p;
+			struct uac1_output_terminal_descriptor *desc = p;
 
 			if (desc->bLength < sizeof(*desc))
 				continue; /* invalid descriptor? */
@@ -1997,7 +2005,7 @@
 	list_for_each_entry(mixer, &chip->mixer_list, list) {
 		snd_iprintf(buffer,
 			"USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n",
-				chip->usb_id, mixer->ctrlif,
+				chip->usb_id, snd_usb_ctrl_intf(chip),
 				mixer->ignore_ctl_error);
 		snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
 		for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
@@ -2115,7 +2123,7 @@
 	int buffer_length;
 	unsigned int epnum;
 
-	hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
+	hostif = mixer->chip->ctrl_intf;
 	/* we need one interrupt input endpoint */
 	if (get_iface_desc(hostif)->bNumEndpoints < 1)
 		return 0;
@@ -2158,7 +2166,6 @@
 	if (!mixer)
 		return -ENOMEM;
 	mixer->chip = chip;
-	mixer->ctrlif = ctrlif;
 	mixer->ignore_ctl_error = ignore_error;
 	mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems),
 				  GFP_KERNEL);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index a7cf100..26c636c 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -3,7 +3,6 @@
 
 struct usb_mixer_interface {
 	struct snd_usb_audio *chip;
-	unsigned int ctrlif;
 	struct list_head list;
 	unsigned int ignore_ctl_error;
 	struct urb *urb;
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
index 1c931b6..ed3e283 100644
--- a/sound/usb/pcm.h
+++ b/sound/usb/pcm.h
@@ -7,8 +7,5 @@
 		       struct usb_host_interface *alts,
 		       struct audioformat *fmt);
 
-int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
-			     struct usb_host_interface *alts,
-			     struct audioformat *fmt, int rate);
 
 #endif /* __USBAUDIO_PCM_H */
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index f8797f6..2e8003f 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2152,91 +2152,7 @@
 	}
 },
 {
-	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7201),
-	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
-		       USB_DEVICE_ID_MATCH_INT_CLASS |
-		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
-	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
-		.vendor_name = "Hauppauge",
-		.product_name = "HVR-950Q",
-		.ifnum = QUIRK_ANY_INTERFACE,
-		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
-	}
-},
-{
-	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7202),
-	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
-		       USB_DEVICE_ID_MATCH_INT_CLASS |
-		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
-	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
-		.vendor_name = "Hauppauge",
-		.product_name = "HVR-950Q",
-		.ifnum = QUIRK_ANY_INTERFACE,
-		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
-	}
-},
-{
-	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7203),
-	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
-		       USB_DEVICE_ID_MATCH_INT_CLASS |
-		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
-	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
-		.vendor_name = "Hauppauge",
-		.product_name = "HVR-950Q",
-		.ifnum = QUIRK_ANY_INTERFACE,
-		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
-	}
-},
-{
-	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7204),
-	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
-		       USB_DEVICE_ID_MATCH_INT_CLASS |
-		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
-	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
-		.vendor_name = "Hauppauge",
-		.product_name = "HVR-950Q",
-		.ifnum = QUIRK_ANY_INTERFACE,
-		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
-	}
-},
-{
-	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7205),
-	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
-		       USB_DEVICE_ID_MATCH_INT_CLASS |
-		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
-	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
-		.vendor_name = "Hauppauge",
-		.product_name = "HVR-950Q",
-		.ifnum = QUIRK_ANY_INTERFACE,
-		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
-	}
-},
-{
-	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7250),
-	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
-		       USB_DEVICE_ID_MATCH_INT_CLASS |
-		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
-	.bInterfaceClass = USB_CLASS_AUDIO,
-	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
-	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
-		.vendor_name = "Hauppauge",
-		.product_name = "HVR-950Q",
-		.ifnum = QUIRK_ANY_INTERFACE,
-		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
-	}
-},
-{
-	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7230),
+	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7240),
 	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
 		       USB_DEVICE_ID_MATCH_INT_CLASS |
 		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
@@ -2249,6 +2165,104 @@
 		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
 	}
 },
+{
+	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7210),
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.vendor_name = "Hauppauge",
+		.product_name = "HVR-950Q",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7217),
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.vendor_name = "Hauppauge",
+		.product_name = "HVR-950Q",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x2040, 0x721b),
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.vendor_name = "Hauppauge",
+		.product_name = "HVR-950Q",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x2040, 0x721e),
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.vendor_name = "Hauppauge",
+		.product_name = "HVR-950Q",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x2040, 0x721f),
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.vendor_name = "Hauppauge",
+		.product_name = "HVR-950Q",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x2040, 0x7280),
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.vendor_name = "Hauppauge",
+		.product_name = "HVR-950Q",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0fd9, 0x0008),
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+	.driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+		.vendor_name = "Hauppauge",
+		.product_name = "HVR-950Q",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_AUDIO_ALIGN_TRANSFER,
+	}
+},
 
 /* Digidesign Mbox */
 {
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index b45e54c..9a9da09 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -32,6 +32,7 @@
 #include "helper.h"
 #include "endpoint.h"
 #include "pcm.h"
+#include "clock.h"
 
 /*
  * handle the quirks for the contained interfaces