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

* 'x86/for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (821 commits)
  x86: make 64bit hpet_set_mapping to use ioremap too, v2
  x86: get x86_phys_bits early
  x86: max_low_pfn_mapped fix #4
  x86: change _node_to_cpumask_ptr to return const ptr
  x86: I/O APIC: remove an IRQ2-mask hack
  x86: fix numaq_tsc_disable calling
  x86, e820: remove end_user_pfn
  x86: max_low_pfn_mapped fix, #3
  x86: max_low_pfn_mapped fix, #2
  x86: max_low_pfn_mapped fix, #1
  x86_64: fix delayed signals
  x86: remove conflicting nx6325 and nx6125 quirks
  x86: Recover timer_ack lost in the merge of the NMI watchdog
  x86: I/O APIC: Never configure IRQ2
  x86: L-APIC: Always fully configure IRQ0
  x86: L-APIC: Set IRQ0 as edge-triggered
  x86: merge dwarf2 headers
  x86: use AS_CFI instead of UNWIND_INFO
  x86: use ignore macro instead of hash comment
  x86: use matching CFI_ENDPROC
  ...
diff --git a/Documentation/ABI/testing/sysfs-block b/Documentation/ABI/testing/sysfs-block
index 4bd9ea5..44f52a4 100644
--- a/Documentation/ABI/testing/sysfs-block
+++ b/Documentation/ABI/testing/sysfs-block
@@ -26,3 +26,37 @@
 		I/O statistics of partition <part>. The format is the
 		same as the above-written /sys/block/<disk>/stat
 		format.
+
+
+What:		/sys/block/<disk>/integrity/format
+Date:		June 2008
+Contact:	Martin K. Petersen <martin.petersen@oracle.com>
+Description:
+		Metadata format for integrity capable block device.
+		E.g. T10-DIF-TYPE1-CRC.
+
+
+What:		/sys/block/<disk>/integrity/read_verify
+Date:		June 2008
+Contact:	Martin K. Petersen <martin.petersen@oracle.com>
+Description:
+		Indicates whether the block layer should verify the
+		integrity of read requests serviced by devices that
+		support sending integrity metadata.
+
+
+What:		/sys/block/<disk>/integrity/tag_size
+Date:		June 2008
+Contact:	Martin K. Petersen <martin.petersen@oracle.com>
+Description:
+		Number of bytes of integrity tag space available per
+		512 bytes of data.
+
+
+What:		/sys/block/<disk>/integrity/write_generate
+Date:		June 2008
+Contact:	Martin K. Petersen <martin.petersen@oracle.com>
+Description:
+		Indicates whether the block layer should automatically
+		generate checksums for write requests bound for
+		devices that support receiving integrity metadata.
diff --git a/Documentation/ABI/testing/sysfs-bus-css b/Documentation/ABI/testing/sysfs-bus-css
new file mode 100644
index 0000000..b585ec2
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-css
@@ -0,0 +1,35 @@
+What:		/sys/bus/css/devices/.../type
+Date:		March 2008
+Contact:	Cornelia Huck <cornelia.huck@de.ibm.com>
+		linux-s390@vger.kernel.org
+Description:	Contains the subchannel type, as reported by the hardware.
+		This attribute is present for all subchannel types.
+
+What:		/sys/bus/css/devices/.../modalias
+Date:		March 2008
+Contact:	Cornelia Huck <cornelia.huck@de.ibm.com>
+		linux-s390@vger.kernel.org
+Description:	Contains the module alias as reported with uevents.
+		It is of the format css:t<type> and present for all
+		subchannel types.
+
+What:		/sys/bus/css/drivers/io_subchannel/.../chpids
+Date:		December 2002
+Contact:	Cornelia Huck <cornelia.huck@de.ibm.com>
+		linux-s390@vger.kernel.org
+Description:	Contains the ids of the channel paths used by this
+		subchannel, as reported by the channel subsystem
+		during subchannel recognition.
+		Note: This is an I/O-subchannel specific attribute.
+Users:		s390-tools, HAL
+
+What:		/sys/bus/css/drivers/io_subchannel/.../pimpampom
+Date:		December 2002
+Contact:	Cornelia Huck <cornelia.huck@de.ibm.com>
+		linux-s390@vger.kernel.org
+Description:	Contains the PIM/PAM/POM values, as reported by the
+		channel subsystem when last queried by the common I/O
+		layer (this implies that this attribute is not neccessarily
+		in sync with the values current in the channel subsystem).
+		Note: This is an I/O-subchannel specific attribute.
+Users:		s390-tools, HAL
diff --git a/Documentation/block/data-integrity.txt b/Documentation/block/data-integrity.txt
new file mode 100644
index 0000000..e9dc8d8
--- /dev/null
+++ b/Documentation/block/data-integrity.txt
@@ -0,0 +1,327 @@
+----------------------------------------------------------------------
+1. INTRODUCTION
+
+Modern filesystems feature checksumming of data and metadata to
+protect against data corruption.  However, the detection of the
+corruption is done at read time which could potentially be months
+after the data was written.  At that point the original data that the
+application tried to write is most likely lost.
+
+The solution is to ensure that the disk is actually storing what the
+application meant it to.  Recent additions to both the SCSI family
+protocols (SBC Data Integrity Field, SCC protection proposal) as well
+as SATA/T13 (External Path Protection) try to remedy this by adding
+support for appending integrity metadata to an I/O.  The integrity
+metadata (or protection information in SCSI terminology) includes a
+checksum for each sector as well as an incrementing counter that
+ensures the individual sectors are written in the right order.  And
+for some protection schemes also that the I/O is written to the right
+place on disk.
+
+Current storage controllers and devices implement various protective
+measures, for instance checksumming and scrubbing.  But these
+technologies are working in their own isolated domains or at best
+between adjacent nodes in the I/O path.  The interesting thing about
+DIF and the other integrity extensions is that the protection format
+is well defined and every node in the I/O path can verify the
+integrity of the I/O and reject it if corruption is detected.  This
+allows not only corruption prevention but also isolation of the point
+of failure.
+
+----------------------------------------------------------------------
+2. THE DATA INTEGRITY EXTENSIONS
+
+As written, the protocol extensions only protect the path between
+controller and storage device.  However, many controllers actually
+allow the operating system to interact with the integrity metadata
+(IMD).  We have been working with several FC/SAS HBA vendors to enable
+the protection information to be transferred to and from their
+controllers.
+
+The SCSI Data Integrity Field works by appending 8 bytes of protection
+information to each sector.  The data + integrity metadata is stored
+in 520 byte sectors on disk.  Data + IMD are interleaved when
+transferred between the controller and target.  The T13 proposal is
+similar.
+
+Because it is highly inconvenient for operating systems to deal with
+520 (and 4104) byte sectors, we approached several HBA vendors and
+encouraged them to allow separation of the data and integrity metadata
+scatter-gather lists.
+
+The controller will interleave the buffers on write and split them on
+read.  This means that the Linux can DMA the data buffers to and from
+host memory without changes to the page cache.
+
+Also, the 16-bit CRC checksum mandated by both the SCSI and SATA specs
+is somewhat heavy to compute in software.  Benchmarks found that
+calculating this checksum had a significant impact on system
+performance for a number of workloads.  Some controllers allow a
+lighter-weight checksum to be used when interfacing with the operating
+system.  Emulex, for instance, supports the TCP/IP checksum instead.
+The IP checksum received from the OS is converted to the 16-bit CRC
+when writing and vice versa.  This allows the integrity metadata to be
+generated by Linux or the application at very low cost (comparable to
+software RAID5).
+
+The IP checksum is weaker than the CRC in terms of detecting bit
+errors.  However, the strength is really in the separation of the data
+buffers and the integrity metadata.  These two distinct buffers much
+match up for an I/O to complete.
+
+The separation of the data and integrity metadata buffers as well as
+the choice in checksums is referred to as the Data Integrity
+Extensions.  As these extensions are outside the scope of the protocol
+bodies (T10, T13), Oracle and its partners are trying to standardize
+them within the Storage Networking Industry Association.
+
+----------------------------------------------------------------------
+3. KERNEL CHANGES
+
+The data integrity framework in Linux enables protection information
+to be pinned to I/Os and sent to/received from controllers that
+support it.
+
+The advantage to the integrity extensions in SCSI and SATA is that
+they enable us to protect the entire path from application to storage
+device.  However, at the same time this is also the biggest
+disadvantage. It means that the protection information must be in a
+format that can be understood by the disk.
+
+Generally Linux/POSIX applications are agnostic to the intricacies of
+the storage devices they are accessing.  The virtual filesystem switch
+and the block layer make things like hardware sector size and
+transport protocols completely transparent to the application.
+
+However, this level of detail is required when preparing the
+protection information to send to a disk.  Consequently, the very
+concept of an end-to-end protection scheme is a layering violation.
+It is completely unreasonable for an application to be aware whether
+it is accessing a SCSI or SATA disk.
+
+The data integrity support implemented in Linux attempts to hide this
+from the application.  As far as the application (and to some extent
+the kernel) is concerned, the integrity metadata is opaque information
+that's attached to the I/O.
+
+The current implementation allows the block layer to automatically
+generate the protection information for any I/O.  Eventually the
+intent is to move the integrity metadata calculation to userspace for
+user data.  Metadata and other I/O that originates within the kernel
+will still use the automatic generation interface.
+
+Some storage devices allow each hardware sector to be tagged with a
+16-bit value.  The owner of this tag space is the owner of the block
+device.  I.e. the filesystem in most cases.  The filesystem can use
+this extra space to tag sectors as they see fit.  Because the tag
+space is limited, the block interface allows tagging bigger chunks by
+way of interleaving.  This way, 8*16 bits of information can be
+attached to a typical 4KB filesystem block.
+
+This also means that applications such as fsck and mkfs will need
+access to manipulate the tags from user space.  A passthrough
+interface for this is being worked on.
+
+
+----------------------------------------------------------------------
+4. BLOCK LAYER IMPLEMENTATION DETAILS
+
+4.1 BIO
+
+The data integrity patches add a new field to struct bio when
+CONFIG_BLK_DEV_INTEGRITY is enabled.  bio->bi_integrity is a pointer
+to a struct bip which contains the bio integrity payload.  Essentially
+a bip is a trimmed down struct bio which holds a bio_vec containing
+the integrity metadata and the required housekeeping information (bvec
+pool, vector count, etc.)
+
+A kernel subsystem can enable data integrity protection on a bio by
+calling bio_integrity_alloc(bio).  This will allocate and attach the
+bip to the bio.
+
+Individual pages containing integrity metadata can subsequently be
+attached using bio_integrity_add_page().
+
+bio_free() will automatically free the bip.
+
+
+4.2 BLOCK DEVICE
+
+Because the format of the protection data is tied to the physical
+disk, each block device has been extended with a block integrity
+profile (struct blk_integrity).  This optional profile is registered
+with the block layer using blk_integrity_register().
+
+The profile contains callback functions for generating and verifying
+the protection data, as well as getting and setting application tags.
+The profile also contains a few constants to aid in completing,
+merging and splitting the integrity metadata.
+
+Layered block devices will need to pick a profile that's appropriate
+for all subdevices.  blk_integrity_compare() can help with that.  DM
+and MD linear, RAID0 and RAID1 are currently supported.  RAID4/5/6
+will require extra work due to the application tag.
+
+
+----------------------------------------------------------------------
+5.0 BLOCK LAYER INTEGRITY API
+
+5.1 NORMAL FILESYSTEM
+
+    The normal filesystem is unaware that the underlying block device
+    is capable of sending/receiving integrity metadata.  The IMD will
+    be automatically generated by the block layer at submit_bio() time
+    in case of a WRITE.  A READ request will cause the I/O integrity
+    to be verified upon completion.
+
+    IMD generation and verification can be toggled using the
+
+      /sys/block/<bdev>/integrity/write_generate
+
+    and
+
+      /sys/block/<bdev>/integrity/read_verify
+
+    flags.
+
+
+5.2 INTEGRITY-AWARE FILESYSTEM
+
+    A filesystem that is integrity-aware can prepare I/Os with IMD
+    attached.  It can also use the application tag space if this is
+    supported by the block device.
+
+
+    int bdev_integrity_enabled(block_device, int rw);
+
+      bdev_integrity_enabled() will return 1 if the block device
+      supports integrity metadata transfer for the data direction
+      specified in 'rw'.
+
+      bdev_integrity_enabled() honors the write_generate and
+      read_verify flags in sysfs and will respond accordingly.
+
+
+    int bio_integrity_prep(bio);
+
+      To generate IMD for WRITE and to set up buffers for READ, the
+      filesystem must call bio_integrity_prep(bio).
+
+      Prior to calling this function, the bio data direction and start
+      sector must be set, and the bio should have all data pages
+      added.  It is up to the caller to ensure that the bio does not
+      change while I/O is in progress.
+
+      bio_integrity_prep() should only be called if
+      bio_integrity_enabled() returned 1.
+
+
+    int bio_integrity_tag_size(bio);
+
+      If the filesystem wants to use the application tag space it will
+      first have to find out how much storage space is available.
+      Because tag space is generally limited (usually 2 bytes per
+      sector regardless of sector size), the integrity framework
+      supports interleaving the information between the sectors in an
+      I/O.
+
+      Filesystems can call bio_integrity_tag_size(bio) to find out how
+      many bytes of storage are available for that particular bio.
+
+      Another option is bdev_get_tag_size(block_device) which will
+      return the number of available bytes per hardware sector.
+
+
+    int bio_integrity_set_tag(bio, void *tag_buf, len);
+
+      After a successful return from bio_integrity_prep(),
+      bio_integrity_set_tag() can be used to attach an opaque tag
+      buffer to a bio.  Obviously this only makes sense if the I/O is
+      a WRITE.
+
+
+    int bio_integrity_get_tag(bio, void *tag_buf, len);
+
+      Similarly, at READ I/O completion time the filesystem can
+      retrieve the tag buffer using bio_integrity_get_tag().
+
+
+6.3 PASSING EXISTING INTEGRITY METADATA
+
+    Filesystems that either generate their own integrity metadata or
+    are capable of transferring IMD from user space can use the
+    following calls:
+
+
+    struct bip * bio_integrity_alloc(bio, gfp_mask, nr_pages);
+
+      Allocates the bio integrity payload and hangs it off of the bio.
+      nr_pages indicate how many pages of protection data need to be
+      stored in the integrity bio_vec list (similar to bio_alloc()).
+
+      The integrity payload will be freed at bio_free() time.
+
+
+    int bio_integrity_add_page(bio, page, len, offset);
+
+      Attaches a page containing integrity metadata to an existing
+      bio.  The bio must have an existing bip,
+      i.e. bio_integrity_alloc() must have been called.  For a WRITE,
+      the integrity metadata in the pages must be in a format
+      understood by the target device with the notable exception that
+      the sector numbers will be remapped as the request traverses the
+      I/O stack.  This implies that the pages added using this call
+      will be modified during I/O!  The first reference tag in the
+      integrity metadata must have a value of bip->bip_sector.
+
+      Pages can be added using bio_integrity_add_page() as long as
+      there is room in the bip bio_vec array (nr_pages).
+
+      Upon completion of a READ operation, the attached pages will
+      contain the integrity metadata received from the storage device.
+      It is up to the receiver to process them and verify data
+      integrity upon completion.
+
+
+6.4 REGISTERING A BLOCK DEVICE AS CAPABLE OF EXCHANGING INTEGRITY
+    METADATA
+
+    To enable integrity exchange on a block device the gendisk must be
+    registered as capable:
+
+    int blk_integrity_register(gendisk, blk_integrity);
+
+      The blk_integrity struct is a template and should contain the
+      following:
+
+        static struct blk_integrity my_profile = {
+            .name                   = "STANDARDSBODY-TYPE-VARIANT-CSUM",
+            .generate_fn            = my_generate_fn,
+       	    .verify_fn              = my_verify_fn,
+       	    .get_tag_fn             = my_get_tag_fn,
+       	    .set_tag_fn             = my_set_tag_fn,
+	    .tuple_size             = sizeof(struct my_tuple_size),
+	    .tag_size               = <tag bytes per hw sector>,
+        };
+
+      'name' is a text string which will be visible in sysfs.  This is
+      part of the userland API so chose it carefully and never change
+      it.  The format is standards body-type-variant.
+      E.g. T10-DIF-TYPE1-IP or T13-EPP-0-CRC.
+
+      'generate_fn' generates appropriate integrity metadata (for WRITE).
+
+      'verify_fn' verifies that the data buffer matches the integrity
+      metadata.
+
+      'tuple_size' must be set to match the size of the integrity
+      metadata per sector.  I.e. 8 for DIF and EPP.
+
+      'tag_size' must be set to identify how many bytes of tag space
+      are available per hardware sector.  For DIF this is either 2 or
+      0 depending on the value of the Control Mode Page ATO bit.
+
+      See 6.2 for a description of get_tag_fn and set_tag_fn.
+
+----------------------------------------------------------------------
+2007-12-24 Martin K. Petersen <martin.petersen@oracle.com>
diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt
index 240ce7a..3bb5f46 100644
--- a/Documentation/ioctl-number.txt
+++ b/Documentation/ioctl-number.txt
@@ -117,6 +117,7 @@
 					<mailto:natalia@nikhefk.nikhef.nl>
 'c'	00-7F	linux/comstats.h	conflict!
 'c'	00-7F	linux/coda.h		conflict!
+'c'	80-9F	asm-s390/chsc.h
 'd'	00-FF	linux/char/drm/drm/h	conflict!
 'd'	00-DF	linux/video_decoder.h	conflict!
 'd'	F0-FF	linux/digi1.h
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 0bbee38..72aff61 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -753,8 +753,11 @@
 
     [Multiple options for each card instance]
     model	- force the model name
-    position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
+    position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
+    bdl_pos_adj	- Specifies the DMA IRQ timing delay in samples.
+		Passing -1 will make the driver to choose the appropriate
+		value based on the controller chip.
     
     [Single (global) options]
     single_cmd  - Use single immediate commands to communicate with
@@ -845,7 +848,7 @@
 	ALC269
 	  basic		Basic preset
 
-	ALC662
+	ALC662/663
 	  3stack-dig	3-stack (2-channel) with SPDIF
 	  3stack-6ch	 3-stack (6-channel)
 	  3stack-6ch-dig 3-stack (6-channel) with SPDIF
@@ -853,6 +856,10 @@
 	  lenovo-101e	 Lenovo laptop
 	  eeepc-p701	ASUS Eeepc P701
 	  eeepc-ep20	ASUS Eeepc EP20
+	  m51va		ASUS M51VA
+	  g71v		ASUS G71V
+	  h13		ASUS H13
+	  g50v		ASUS G50V
 	  auto		auto-config reading BIOS (default)
 
 	ALC882/885
@@ -1091,7 +1098,7 @@
     This occurs when the access to non-existing or non-working codec slot
     (likely a modem one) causes a stall of the communication via HD-audio
     bus.  You can see which codec slots are probed by enabling
-    CONFIG_SND_DEBUG_DETECT, or simply from the file name of the codec
+    CONFIG_SND_DEBUG_VERBOSE, or simply from the file name of the codec
     proc files.  Then limit the slots to probe by probe_mask option.
     For example, probe_mask=1 means to probe only the first slot, and
     probe_mask=4 means only the third slot.
@@ -2267,6 +2274,10 @@
 other driver (e.g. snd-usb-audio) is loaded before snd-interwave or
 snd-ens1371, it will be assigned to the third or later slot.
 
+When a module name is given with '!', the slot will be given for any
+modules but that name.  For example, "slots=!snd-pcsp" will reserve
+the first slot for any modules but snd-pcsp. 
+
 
 ALSA PCM devices to OSS devices mapping
 =======================================
diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
index b03df4d..e13c4e6 100644
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
@@ -6127,8 +6127,8 @@
 
       <para>
         <function>snd_printdd()</function> is compiled in only when
-      <constant>CONFIG_SND_DEBUG_DETECT</constant> is set. Please note
-      that <constant>DEBUG_DETECT</constant> is not set as default
+      <constant>CONFIG_SND_DEBUG_VERBOSE</constant> is set. Please note
+      that <constant>CONFIG_SND_DEBUG_VERBOSE</constant> is not set as default
       even if you configure the alsa-driver with
       <option>--with-debug=full</option> option. You need to give
       explicitly <option>--with-debug=detect</option> option instead. 
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
index 09ad799..45d63c9 100644
--- a/arch/avr32/Kconfig
+++ b/arch/avr32/Kconfig
@@ -88,6 +88,7 @@
 	select MMU
 	select PERFORMANCE_COUNTERS
 	select HAVE_GPIO_LIB
+	select GENERIC_ALLOCATOR
 
 #
 # CPU types
@@ -147,6 +148,9 @@
 
 source "kernel/Kconfig.preempt"
 
+config QUICKLIST
+	def_bool y
+
 config HAVE_ARCH_BOOTMEM_NODE
 	def_bool n
 
@@ -201,6 +205,11 @@
 
 menu "Power management options"
 
+source "kernel/power/Kconfig"
+
+config ARCH_SUSPEND_POSSIBLE
+	def_bool y
+
 menu "CPU Frequency scaling"
 
 source "drivers/cpufreq/Kconfig"
diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c
index a398be2..a51bb9f 100644
--- a/arch/avr32/boards/atngw100/setup.c
+++ b/arch/avr32/boards/atngw100/setup.c
@@ -9,6 +9,8 @@
  */
 #include <linux/clk.h>
 #include <linux/etherdevice.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
 #include <linux/i2c-gpio.h>
 #include <linux/init.h>
 #include <linux/linkage.h>
@@ -25,6 +27,13 @@
 #include <asm/arch/init.h>
 #include <asm/arch/portmux.h>
 
+/* Oscillator frequencies. These are board-specific */
+unsigned long at32_board_osc_rates[3] = {
+	[0] = 32768,	/* 32.768 kHz on RTC osc */
+	[1] = 20000000,	/* 20 MHz on osc0 */
+	[2] = 12000000,	/* 12 MHz on osc1 */
+};
+
 /* Initialized by bootloader-specific startup code. */
 struct tag *bootloader_tags __initdata;
 
@@ -140,6 +149,10 @@
 	},
 };
 
+static struct i2c_board_info __initdata i2c_info[] = {
+	/* NOTE:  original ATtiny24 firmware is at address 0x0b */
+};
+
 static int __init atngw100_init(void)
 {
 	unsigned	i;
@@ -165,12 +178,28 @@
 	}
 	platform_device_register(&ngw_gpio_leds);
 
+	/* all these i2c/smbus pins should have external pullups for
+	 * open-drain sharing among all I2C devices.  SDA and SCL do;
+	 * PB28/EXTINT3 doesn't; it should be SMBALERT# (for PMBus),
+	 * but it's not available off-board.
+	 */
+	at32_select_periph(GPIO_PIN_PB(28), 0, AT32_GPIOF_PULLUP);
 	at32_select_gpio(i2c_gpio_data.sda_pin,
 		AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
 	at32_select_gpio(i2c_gpio_data.scl_pin,
 		AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
 	platform_device_register(&i2c_gpio_device);
+	i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
 
 	return 0;
 }
 postcore_initcall(atngw100_init);
+
+static int __init atngw100_arch_init(void)
+{
+	/* set_irq_type() after the arch_initcall for EIC has run, and
+	 * before the I2C subsystem could try using this IRQ.
+	 */
+	return set_irq_type(AT32_EXTINT(3), IRQ_TYPE_EDGE_FALLING);
+}
+arch_initcall(atngw100_arch_init);
diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c
index 000eb42..86b363c 100644
--- a/arch/avr32/boards/atstk1000/atstk1002.c
+++ b/arch/avr32/boards/atstk1000/atstk1002.c
@@ -28,6 +28,12 @@
 
 #include "atstk1000.h"
 
+/* Oscillator frequencies. These are board specific */
+unsigned long at32_board_osc_rates[3] = {
+	[0] = 32768,	/* 32.768 kHz on RTC osc */
+	[1] = 20000000,	/* 20 MHz on osc0 */
+	[2] = 12000000,	/* 12 MHz on osc1 */
+};
 
 struct eth_addr {
 	u8 addr[6];
@@ -232,7 +238,7 @@
 	set_hw_addr(at32_add_device_eth(1, &eth_data[1]));
 #else
 	at32_add_device_lcdc(0, &atstk1000_lcdc_data,
-			     fbmem_start, fbmem_size);
+			     fbmem_start, fbmem_size, 0);
 #endif
 	at32_add_device_usba(0, NULL);
 #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c
index a0b223d..ea109f4 100644
--- a/arch/avr32/boards/atstk1000/atstk1003.c
+++ b/arch/avr32/boards/atstk1000/atstk1003.c
@@ -27,6 +27,13 @@
 
 #include "atstk1000.h"
 
+/* Oscillator frequencies. These are board specific */
+unsigned long at32_board_osc_rates[3] = {
+	[0] = 32768,	/* 32.768 kHz on RTC osc */
+	[1] = 20000000,	/* 20 MHz on osc0 */
+	[2] = 12000000,	/* 12 MHz on osc1 */
+};
+
 #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
 static struct at73c213_board_info at73c213_data = {
 	.ssc_id		= 0,
diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c
index e765a86..c7236df 100644
--- a/arch/avr32/boards/atstk1000/atstk1004.c
+++ b/arch/avr32/boards/atstk1000/atstk1004.c
@@ -29,6 +29,13 @@
 
 #include "atstk1000.h"
 
+/* Oscillator frequencies. These are board specific */
+unsigned long at32_board_osc_rates[3] = {
+	[0] = 32768,	/* 32.768 kHz on RTC osc */
+	[1] = 20000000,	/* 20 MHz on osc0 */
+	[2] = 12000000,	/* 12 MHz on osc1 */
+};
+
 #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
 static struct at73c213_board_info at73c213_data = {
 	.ssc_id		= 0,
@@ -133,7 +140,7 @@
 	at32_add_device_mci(0);
 #endif
 	at32_add_device_lcdc(0, &atstk1000_lcdc_data,
-			     fbmem_start, fbmem_size);
+			     fbmem_start, fbmem_size, 0);
 	at32_add_device_usba(0, NULL);
 #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
 	at32_add_device_ssc(0, ATMEL_SSC_TX);
diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S
index 5f31702..2b398ca 100644
--- a/arch/avr32/kernel/entry-avr32b.S
+++ b/arch/avr32/kernel/entry-avr32b.S
@@ -74,50 +74,41 @@
 	.align	2
 	bral	do_dtlb_modified
 
-	/*
-	 * r0 :	PGD/PT/PTE
-	 * r1 : Offending address
-	 * r2 : Scratch register
-	 * r3 : Cause (5, 12 or 13)
-	 */
 #define	tlbmiss_save	pushm	r0-r3
 #define tlbmiss_restore	popm	r0-r3
 
-	.section .tlbx.ex.text,"ax",@progbits
+	.org	0x50
 	.global	itlb_miss
 itlb_miss:
 	tlbmiss_save
 	rjmp	tlb_miss_common
 
-	.section .tlbr.ex.text,"ax",@progbits
+	.org	0x60
 dtlb_miss_read:
 	tlbmiss_save
 	rjmp	tlb_miss_common
 
-	.section .tlbw.ex.text,"ax",@progbits
+	.org	0x70
 dtlb_miss_write:
 	tlbmiss_save
 
 	.global	tlb_miss_common
+	.align	2
 tlb_miss_common:
 	mfsr	r0, SYSREG_TLBEAR
 	mfsr	r1, SYSREG_PTBR
 
-	/* Is it the vmalloc space? */
-	bld	r0, 31
-	brcs	handle_vmalloc_miss
-
-	/* First level lookup */
+	/*
+	 * First level lookup: The PGD contains virtual pointers to
+	 * the second-level page tables, but they may be NULL if not
+	 * present.
+	 */
 pgtbl_lookup:
 	lsr	r2, r0, PGDIR_SHIFT
 	ld.w	r3, r1[r2 << 2]
 	bfextu	r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT
-	bld	r3, _PAGE_BIT_PRESENT
-	brcc	page_table_not_present
-
-	/* Translate to virtual address in P1. */
-	andl	r3, 0xf000
-	sbr	r3, 31
+	cp.w	r3, 0
+	breq	page_table_not_present
 
 	/* Second level lookup */
 	ld.w	r2, r3[r1 << 2]
@@ -148,16 +139,55 @@
 	tlbmiss_restore
 	rete
 
-handle_vmalloc_miss:
-	/* Simply do the lookup in init's page table */
+	/* The slow path of the TLB miss handler */
+	.align	2
+page_table_not_present:
+	/* Do we need to synchronize with swapper_pg_dir? */
+	bld	r0, 31
+	brcs	sync_with_swapper_pg_dir
+
+page_not_present:
+	tlbmiss_restore
+	sub	sp, 4
+	stmts	--sp, r0-lr
+	rcall	save_full_context_ex
+	mfsr	r12, SYSREG_ECR
+	mov	r11, sp
+	rcall	do_page_fault
+	rjmp	ret_from_exception
+
+	.align	2
+sync_with_swapper_pg_dir:
+	/*
+	 * If swapper_pg_dir contains a non-NULL second-level page
+	 * table pointer, copy it into the current PGD. If not, we
+	 * must handle it as a full-blown page fault.
+	 *
+	 * Jumping back to pgtbl_lookup causes an unnecessary lookup,
+	 * but it is guaranteed to be a cache hit, it won't happen
+	 * very often, and we absolutely do not want to sacrifice any
+	 * performance in the fast path in order to improve this.
+	 */
 	mov	r1, lo(swapper_pg_dir)
 	orh	r1, hi(swapper_pg_dir)
+	ld.w	r3, r1[r2 << 2]
+	cp.w	r3, 0
+	breq	page_not_present
+	mfsr	r1, SYSREG_PTBR
+	st.w	r1[r2 << 2], r3
 	rjmp	pgtbl_lookup
 
+	/*
+	 * We currently have two bytes left at this point until we
+	 * crash into the system call handler...
+	 *
+	 * Don't worry, the assembler will let us know.
+	 */
+
 
 	/* ---                    System Call                    --- */
 
-	.section .scall.text,"ax",@progbits
+	.org	0x100
 system_call:
 #ifdef CONFIG_PREEMPT
 	mask_interrupts
@@ -266,18 +296,6 @@
 	brcc	syscall_exit_cont
 	rjmp	enter_monitor_mode
 
-	/* The slow path of the TLB miss handler */
-page_table_not_present:
-page_not_present:
-	tlbmiss_restore
-	sub	sp, 4
-	stmts	--sp, r0-lr
-	rcall	save_full_context_ex
-	mfsr	r12, SYSREG_ECR
-	mov	r11, sp
-	rcall	do_page_fault
-	rjmp	ret_from_exception
-
 	/* This function expects to find offending PC in SYSREG_RAR_EX */
 	.type	save_full_context_ex, @function
 	.align	2
diff --git a/arch/avr32/kernel/signal.c b/arch/avr32/kernel/signal.c
index 5616a00..c5b11f9 100644
--- a/arch/avr32/kernel/signal.c
+++ b/arch/avr32/kernel/signal.c
@@ -93,6 +93,9 @@
 	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
 		goto badframe;
 
+	if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT)
+		goto badframe;
+
 	pr_debug("Context restored: pc = %08lx, lr = %08lx, sp = %08lx\n",
 		 regs->pc, regs->lr, regs->sp);
 
diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c
index 00a9862..abd954f 100644
--- a/arch/avr32/kernel/time.c
+++ b/arch/avr32/kernel/time.c
@@ -7,21 +7,13 @@
  */
 #include <linux/clk.h>
 #include <linux/clockchips.h>
-#include <linux/time.h>
-#include <linux/module.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
-#include <linux/kernel_stat.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/profile.h>
-#include <linux/sysdev.h>
-#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
 
-#include <asm/div64.h>
 #include <asm/sysreg.h>
-#include <asm/io.h>
-#include <asm/sections.h>
 
 #include <asm/arch/pm.h>
 
diff --git a/arch/avr32/kernel/vmlinux.lds.S b/arch/avr32/kernel/vmlinux.lds.S
index 481cfd4..5d25d8e 100644
--- a/arch/avr32/kernel/vmlinux.lds.S
+++ b/arch/avr32/kernel/vmlinux.lds.S
@@ -68,14 +68,6 @@
 		_evba = .;
 		_text = .;
 		*(.ex.text)
-		. = 0x50;
-		*(.tlbx.ex.text)
-		. = 0x60;
-		*(.tlbr.ex.text)
-		. = 0x70;
-		*(.tlbw.ex.text)
-		. = 0x100;
-		*(.scall.text)
 		*(.irq.text)
 		KPROBES_TEXT
 		TEXT_TEXT
@@ -107,6 +99,10 @@
 		 */
 		*(.data.init_task)
 
+		/* Then, the page-aligned data */
+		. = ALIGN(PAGE_SIZE);
+		*(.data.page_aligned)
+
 		/* Then, the cacheline aligned data */
 		. = ALIGN(L1_CACHE_BYTES);
 		*(.data.cacheline_aligned)
diff --git a/arch/avr32/lib/io-readsb.S b/arch/avr32/lib/io-readsb.S
index 2be5da7..cb2d869 100644
--- a/arch/avr32/lib/io-readsb.S
+++ b/arch/avr32/lib/io-readsb.S
@@ -41,7 +41,7 @@
 2:	sub	r10, -4
 	reteq	r12
 
-3:	ld.uh	r8, r12[0]
+3:	ld.ub	r8, r12[0]
 	sub	r10, 1
 	st.b	r11++, r8
 	brne	3b
diff --git a/arch/avr32/mach-at32ap/Makefile b/arch/avr32/mach-at32ap/Makefile
index e890094..d5018e2 100644
--- a/arch/avr32/mach-at32ap/Makefile
+++ b/arch/avr32/mach-at32ap/Makefile
@@ -1,3 +1,8 @@
-obj-y				+= at32ap.o clock.o intc.o extint.o pio.o hsmc.o
+obj-y				+= pdc.o clock.o intc.o extint.o pio.o hsmc.o
 obj-$(CONFIG_CPU_AT32AP700X)	+= at32ap700x.o pm-at32ap700x.o
 obj-$(CONFIG_CPU_FREQ_AT32AP)	+= cpufreq.o
+obj-$(CONFIG_PM)		+= pm.o
+
+ifeq ($(CONFIG_PM_DEBUG),y)
+CFLAGS_pm.o	+= -DDEBUG
+endif
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index 0f24b4f..07b21b1 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -20,6 +20,7 @@
 #include <asm/arch/at32ap700x.h>
 #include <asm/arch/board.h>
 #include <asm/arch/portmux.h>
+#include <asm/arch/sram.h>
 
 #include <video/atmel_lcdc.h>
 
@@ -93,19 +94,12 @@
 
 static DEFINE_SPINLOCK(pm_lock);
 
-unsigned long at32ap7000_osc_rates[3] = {
-	[0] = 32768,
-	/* FIXME: these are ATSTK1002-specific */
-	[1] = 20000000,
-	[2] = 12000000,
-};
-
 static struct clk osc0;
 static struct clk osc1;
 
 static unsigned long osc_get_rate(struct clk *clk)
 {
-	return at32ap7000_osc_rates[clk->index];
+	return at32_board_osc_rates[clk->index];
 }
 
 static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
@@ -682,6 +676,14 @@
 	.users		= 1,
 	.index		= 3,
 };
+static struct clk sdramc_clk = {
+	.name		= "sdramc_clk",
+	.parent		= &pbb_clk,
+	.mode		= pbb_clk_mode,
+	.get_rate	= pbb_clk_get_rate,
+	.users		= 1,
+	.index		= 14,
+};
 
 static struct resource smc0_resource[] = {
 	PBMEM(0xfff03400),
@@ -841,6 +843,81 @@
 }
 
 /* --------------------------------------------------------------------
+ *  PSIF
+ * -------------------------------------------------------------------- */
+static struct resource atmel_psif0_resource[] __initdata = {
+	{
+		.start	= 0xffe03c00,
+		.end	= 0xffe03cff,
+		.flags	= IORESOURCE_MEM,
+	},
+	IRQ(18),
+};
+static struct clk atmel_psif0_pclk = {
+	.name		= "pclk",
+	.parent		= &pba_clk,
+	.mode		= pba_clk_mode,
+	.get_rate	= pba_clk_get_rate,
+	.index		= 15,
+};
+
+static struct resource atmel_psif1_resource[] __initdata = {
+	{
+		.start	= 0xffe03d00,
+		.end	= 0xffe03dff,
+		.flags	= IORESOURCE_MEM,
+	},
+	IRQ(18),
+};
+static struct clk atmel_psif1_pclk = {
+	.name		= "pclk",
+	.parent		= &pba_clk,
+	.mode		= pba_clk_mode,
+	.get_rate	= pba_clk_get_rate,
+	.index		= 15,
+};
+
+struct platform_device *__init at32_add_device_psif(unsigned int id)
+{
+	struct platform_device *pdev;
+
+	if (!(id == 0 || id == 1))
+		return NULL;
+
+	pdev = platform_device_alloc("atmel_psif", id);
+	if (!pdev)
+		return NULL;
+
+	switch (id) {
+	case 0:
+		if (platform_device_add_resources(pdev, atmel_psif0_resource,
+					ARRAY_SIZE(atmel_psif0_resource)))
+			goto err_add_resources;
+		atmel_psif0_pclk.dev = &pdev->dev;
+		select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */
+		select_peripheral(PA(9), PERIPH_A, 0); /* DATA  */
+		break;
+	case 1:
+		if (platform_device_add_resources(pdev, atmel_psif1_resource,
+					ARRAY_SIZE(atmel_psif1_resource)))
+			goto err_add_resources;
+		atmel_psif1_pclk.dev = &pdev->dev;
+		select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */
+		select_peripheral(PB(12), PERIPH_A, 0); /* DATA  */
+		break;
+	default:
+		return NULL;
+	}
+
+	platform_device_add(pdev);
+	return pdev;
+
+err_add_resources:
+	platform_device_put(pdev);
+	return NULL;
+}
+
+/* --------------------------------------------------------------------
  *  USART
  * -------------------------------------------------------------------- */
 
@@ -1113,7 +1190,8 @@
 	switch (id) {
 	case 0:
 		pdev = &atmel_spi0_device;
-		select_peripheral(PA(0),  PERIPH_A, 0);	/* MISO	 */
+		/* pullup MISO so a level is always defined */
+		select_peripheral(PA(0),  PERIPH_A, AT32_GPIOF_PULLUP);
 		select_peripheral(PA(1),  PERIPH_A, 0);	/* MOSI	 */
 		select_peripheral(PA(2),  PERIPH_A, 0);	/* SCK	 */
 		at32_spi_setup_slaves(0, b, n, spi0_pins);
@@ -1121,7 +1199,8 @@
 
 	case 1:
 		pdev = &atmel_spi1_device;
-		select_peripheral(PB(0),  PERIPH_B, 0);	/* MISO  */
+		/* pullup MISO so a level is always defined */
+		select_peripheral(PB(0),  PERIPH_B, AT32_GPIOF_PULLUP);
 		select_peripheral(PB(1),  PERIPH_B, 0);	/* MOSI  */
 		select_peripheral(PB(5),  PERIPH_B, 0);	/* SCK   */
 		at32_spi_setup_slaves(1, b, n, spi1_pins);
@@ -1264,7 +1343,8 @@
 
 struct platform_device *__init
 at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
-		     unsigned long fbmem_start, unsigned long fbmem_len)
+		     unsigned long fbmem_start, unsigned long fbmem_len,
+		     unsigned int pin_config)
 {
 	struct platform_device *pdev;
 	struct atmel_lcdfb_info *info;
@@ -1291,37 +1371,77 @@
 	switch (id) {
 	case 0:
 		pdev = &atmel_lcdfb0_device;
-		select_peripheral(PC(19), PERIPH_A, 0);	/* CC	  */
-		select_peripheral(PC(20), PERIPH_A, 0);	/* HSYNC  */
-		select_peripheral(PC(21), PERIPH_A, 0);	/* PCLK	  */
-		select_peripheral(PC(22), PERIPH_A, 0);	/* VSYNC  */
-		select_peripheral(PC(23), PERIPH_A, 0);	/* DVAL	  */
-		select_peripheral(PC(24), PERIPH_A, 0);	/* MODE	  */
-		select_peripheral(PC(25), PERIPH_A, 0);	/* PWR	  */
-		select_peripheral(PC(26), PERIPH_A, 0);	/* DATA0  */
-		select_peripheral(PC(27), PERIPH_A, 0);	/* DATA1  */
-		select_peripheral(PC(28), PERIPH_A, 0);	/* DATA2  */
-		select_peripheral(PC(29), PERIPH_A, 0);	/* DATA3  */
-		select_peripheral(PC(30), PERIPH_A, 0);	/* DATA4  */
-		select_peripheral(PC(31), PERIPH_A, 0);	/* DATA5  */
-		select_peripheral(PD(0),  PERIPH_A, 0);	/* DATA6  */
-		select_peripheral(PD(1),  PERIPH_A, 0);	/* DATA7  */
-		select_peripheral(PD(2),  PERIPH_A, 0);	/* DATA8  */
-		select_peripheral(PD(3),  PERIPH_A, 0);	/* DATA9  */
-		select_peripheral(PD(4),  PERIPH_A, 0);	/* DATA10 */
-		select_peripheral(PD(5),  PERIPH_A, 0);	/* DATA11 */
-		select_peripheral(PD(6),  PERIPH_A, 0);	/* DATA12 */
-		select_peripheral(PD(7),  PERIPH_A, 0);	/* DATA13 */
-		select_peripheral(PD(8),  PERIPH_A, 0);	/* DATA14 */
-		select_peripheral(PD(9),  PERIPH_A, 0);	/* DATA15 */
-		select_peripheral(PD(10), PERIPH_A, 0);	/* DATA16 */
-		select_peripheral(PD(11), PERIPH_A, 0);	/* DATA17 */
-		select_peripheral(PD(12), PERIPH_A, 0);	/* DATA18 */
-		select_peripheral(PD(13), PERIPH_A, 0);	/* DATA19 */
-		select_peripheral(PD(14), PERIPH_A, 0);	/* DATA20 */
-		select_peripheral(PD(15), PERIPH_A, 0);	/* DATA21 */
-		select_peripheral(PD(16), PERIPH_A, 0);	/* DATA22 */
-		select_peripheral(PD(17), PERIPH_A, 0);	/* DATA23 */
+
+		switch (pin_config) {
+		case 0:
+			select_peripheral(PC(19), PERIPH_A, 0);	/* CC	  */
+			select_peripheral(PC(20), PERIPH_A, 0);	/* HSYNC  */
+			select_peripheral(PC(21), PERIPH_A, 0);	/* PCLK	  */
+			select_peripheral(PC(22), PERIPH_A, 0);	/* VSYNC  */
+			select_peripheral(PC(23), PERIPH_A, 0);	/* DVAL	  */
+			select_peripheral(PC(24), PERIPH_A, 0);	/* MODE	  */
+			select_peripheral(PC(25), PERIPH_A, 0);	/* PWR	  */
+			select_peripheral(PC(26), PERIPH_A, 0);	/* DATA0  */
+			select_peripheral(PC(27), PERIPH_A, 0);	/* DATA1  */
+			select_peripheral(PC(28), PERIPH_A, 0);	/* DATA2  */
+			select_peripheral(PC(29), PERIPH_A, 0);	/* DATA3  */
+			select_peripheral(PC(30), PERIPH_A, 0);	/* DATA4  */
+			select_peripheral(PC(31), PERIPH_A, 0);	/* DATA5  */
+			select_peripheral(PD(0),  PERIPH_A, 0);	/* DATA6  */
+			select_peripheral(PD(1),  PERIPH_A, 0);	/* DATA7  */
+			select_peripheral(PD(2),  PERIPH_A, 0);	/* DATA8  */
+			select_peripheral(PD(3),  PERIPH_A, 0);	/* DATA9  */
+			select_peripheral(PD(4),  PERIPH_A, 0);	/* DATA10 */
+			select_peripheral(PD(5),  PERIPH_A, 0);	/* DATA11 */
+			select_peripheral(PD(6),  PERIPH_A, 0);	/* DATA12 */
+			select_peripheral(PD(7),  PERIPH_A, 0);	/* DATA13 */
+			select_peripheral(PD(8),  PERIPH_A, 0);	/* DATA14 */
+			select_peripheral(PD(9),  PERIPH_A, 0);	/* DATA15 */
+			select_peripheral(PD(10), PERIPH_A, 0);	/* DATA16 */
+			select_peripheral(PD(11), PERIPH_A, 0);	/* DATA17 */
+			select_peripheral(PD(12), PERIPH_A, 0);	/* DATA18 */
+			select_peripheral(PD(13), PERIPH_A, 0);	/* DATA19 */
+			select_peripheral(PD(14), PERIPH_A, 0);	/* DATA20 */
+			select_peripheral(PD(15), PERIPH_A, 0);	/* DATA21 */
+			select_peripheral(PD(16), PERIPH_A, 0);	/* DATA22 */
+			select_peripheral(PD(17), PERIPH_A, 0);	/* DATA23 */
+			break;
+		case 1:
+			select_peripheral(PE(0),  PERIPH_B, 0);	/* CC	  */
+			select_peripheral(PC(20), PERIPH_A, 0);	/* HSYNC  */
+			select_peripheral(PC(21), PERIPH_A, 0);	/* PCLK	  */
+			select_peripheral(PC(22), PERIPH_A, 0);	/* VSYNC  */
+			select_peripheral(PE(1),  PERIPH_B, 0);	/* DVAL	  */
+			select_peripheral(PE(2),  PERIPH_B, 0);	/* MODE	  */
+			select_peripheral(PC(25), PERIPH_A, 0);	/* PWR	  */
+			select_peripheral(PE(3),  PERIPH_B, 0);	/* DATA0  */
+			select_peripheral(PE(4),  PERIPH_B, 0);	/* DATA1  */
+			select_peripheral(PE(5),  PERIPH_B, 0);	/* DATA2  */
+			select_peripheral(PE(6),  PERIPH_B, 0);	/* DATA3  */
+			select_peripheral(PE(7),  PERIPH_B, 0);	/* DATA4  */
+			select_peripheral(PC(31), PERIPH_A, 0);	/* DATA5  */
+			select_peripheral(PD(0),  PERIPH_A, 0);	/* DATA6  */
+			select_peripheral(PD(1),  PERIPH_A, 0);	/* DATA7  */
+			select_peripheral(PE(8),  PERIPH_B, 0);	/* DATA8  */
+			select_peripheral(PE(9),  PERIPH_B, 0);	/* DATA9  */
+			select_peripheral(PE(10), PERIPH_B, 0);	/* DATA10 */
+			select_peripheral(PE(11), PERIPH_B, 0);	/* DATA11 */
+			select_peripheral(PE(12), PERIPH_B, 0);	/* DATA12 */
+			select_peripheral(PD(7),  PERIPH_A, 0);	/* DATA13 */
+			select_peripheral(PD(8),  PERIPH_A, 0);	/* DATA14 */
+			select_peripheral(PD(9),  PERIPH_A, 0);	/* DATA15 */
+			select_peripheral(PE(13), PERIPH_B, 0);	/* DATA16 */
+			select_peripheral(PE(14), PERIPH_B, 0);	/* DATA17 */
+			select_peripheral(PE(15), PERIPH_B, 0);	/* DATA18 */
+			select_peripheral(PE(16), PERIPH_B, 0);	/* DATA19 */
+			select_peripheral(PE(17), PERIPH_B, 0);	/* DATA20 */
+			select_peripheral(PE(18), PERIPH_B, 0);	/* DATA21 */
+			select_peripheral(PD(16), PERIPH_A, 0);	/* DATA22 */
+			select_peripheral(PD(17), PERIPH_A, 0);	/* DATA23 */
+			break;
+		default:
+			goto err_invalid_id;
+		}
 
 		clk_set_parent(&atmel_lcdfb0_pixclk, &pll0);
 		clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0));
@@ -1360,7 +1480,7 @@
 	IRQ(24),
 };
 static struct clk atmel_pwm0_mck = {
-	.name		= "mck",
+	.name		= "pwm_clk",
 	.parent		= &pbb_clk,
 	.mode		= pbb_clk_mode,
 	.get_rate	= pbb_clk_get_rate,
@@ -1887,6 +2007,7 @@
 	&hmatrix_clk,
 	&ebi_clk,
 	&hramc_clk,
+	&sdramc_clk,
 	&smc0_pclk,
 	&smc0_mck,
 	&pdc_hclk,
@@ -1900,6 +2021,8 @@
 	&pio4_mck,
 	&at32_tcb0_t0_clk,
 	&at32_tcb1_t0_clk,
+	&atmel_psif0_pclk,
+	&atmel_psif1_pclk,
 	&atmel_usart0_usart,
 	&atmel_usart1_usart,
 	&atmel_usart2_usart,
@@ -1935,16 +2058,7 @@
 };
 unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
 
-void __init at32_portmux_init(void)
-{
-	at32_init_pio(&pio0_device);
-	at32_init_pio(&pio1_device);
-	at32_init_pio(&pio2_device);
-	at32_init_pio(&pio3_device);
-	at32_init_pio(&pio4_device);
-}
-
-void __init at32_clock_init(void)
+void __init setup_platform(void)
 {
 	u32 cpu_mask = 0, hsb_mask = 0, pba_mask = 0, pbb_mask = 0;
 	int i;
@@ -1999,4 +2113,36 @@
 	pm_writel(HSB_MASK, hsb_mask);
 	pm_writel(PBA_MASK, pba_mask);
 	pm_writel(PBB_MASK, pbb_mask);
+
+	/* Initialize the port muxes */
+	at32_init_pio(&pio0_device);
+	at32_init_pio(&pio1_device);
+	at32_init_pio(&pio2_device);
+	at32_init_pio(&pio3_device);
+	at32_init_pio(&pio4_device);
 }
+
+struct gen_pool *sram_pool;
+
+static int __init sram_init(void)
+{
+	struct gen_pool *pool;
+
+	/* 1KiB granularity */
+	pool = gen_pool_create(10, -1);
+	if (!pool)
+		goto fail;
+
+	if (gen_pool_add(pool, 0x24000000, 0x8000, -1))
+		goto err_pool_add;
+
+	sram_pool = pool;
+	return 0;
+
+err_pool_add:
+	gen_pool_destroy(pool);
+fail:
+	pr_err("Failed to create SRAM pool\n");
+	return -ENOMEM;
+}
+core_initcall(sram_init);
diff --git a/arch/avr32/mach-at32ap/intc.c b/arch/avr32/mach-at32ap/intc.c
index 097cf4e..994c4545 100644
--- a/arch/avr32/mach-at32ap/intc.c
+++ b/arch/avr32/mach-at32ap/intc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 Atmel Corporation
+ * Copyright (C) 2006, 2008 Atmel Corporation
  *
  * 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
@@ -12,14 +12,20 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/platform_device.h>
+#include <linux/sysdev.h>
 
 #include <asm/io.h>
 
 #include "intc.h"
 
 struct intc {
-	void __iomem	*regs;
-	struct irq_chip	chip;
+	void __iomem		*regs;
+	struct irq_chip		chip;
+	struct sys_device	sysdev;
+#ifdef CONFIG_PM
+	unsigned long		suspend_ipr;
+	unsigned long		saved_ipr[64];
+#endif
 };
 
 extern struct platform_device at32_intc0_device;
@@ -136,6 +142,74 @@
 	panic("Interrupt controller initialization failed!\n");
 }
 
+#ifdef CONFIG_PM
+void intc_set_suspend_handler(unsigned long offset)
+{
+	intc0.suspend_ipr = offset;
+}
+
+static int intc_suspend(struct sys_device *sdev, pm_message_t state)
+{
+	struct intc *intc = container_of(sdev, struct intc, sysdev);
+	int i;
+
+	if (unlikely(!irqs_disabled())) {
+		pr_err("intc_suspend: called with interrupts enabled\n");
+		return -EINVAL;
+	}
+
+	if (unlikely(!intc->suspend_ipr)) {
+		pr_err("intc_suspend: suspend_ipr not initialized\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < 64; i++) {
+		intc->saved_ipr[i] = intc_readl(intc, INTPR0 + 4 * i);
+		intc_writel(intc, INTPR0 + 4 * i, intc->suspend_ipr);
+	}
+
+	return 0;
+}
+
+static int intc_resume(struct sys_device *sdev)
+{
+	struct intc *intc = container_of(sdev, struct intc, sysdev);
+	int i;
+
+	WARN_ON(!irqs_disabled());
+
+	for (i = 0; i < 64; i++)
+		intc_writel(intc, INTPR0 + 4 * i, intc->saved_ipr[i]);
+
+	return 0;
+}
+#else
+#define intc_suspend	NULL
+#define intc_resume	NULL
+#endif
+
+static struct sysdev_class intc_class = {
+	.name		= "intc",
+	.suspend	= intc_suspend,
+	.resume		= intc_resume,
+};
+
+static int __init intc_init_sysdev(void)
+{
+	int ret;
+
+	ret = sysdev_class_register(&intc_class);
+	if (ret)
+		return ret;
+
+	intc0.sysdev.id = 0;
+	intc0.sysdev.cls = &intc_class;
+	ret = sysdev_register(&intc0.sysdev);
+
+	return ret;
+}
+device_initcall(intc_init_sysdev);
+
 unsigned long intc_get_pending(unsigned int group)
 {
 	return intc_readl(&intc0, INTREQ0 + 4 * group);
diff --git a/arch/avr32/mach-at32ap/at32ap.c b/arch/avr32/mach-at32ap/pdc.c
similarity index 90%
rename from arch/avr32/mach-at32ap/at32ap.c
rename to arch/avr32/mach-at32ap/pdc.c
index 7c4987f..1040bda 100644
--- a/arch/avr32/mach-at32ap/at32ap.c
+++ b/arch/avr32/mach-at32ap/pdc.c
@@ -11,14 +11,6 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 
-#include <asm/arch/init.h>
-
-void __init setup_platform(void)
-{
-	at32_clock_init();
-	at32_portmux_init();
-}
-
 static int __init pdc_probe(struct platform_device *pdev)
 {
 	struct clk *pclk, *hclk;
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c
index 38a8fa3..60da03b 100644
--- a/arch/avr32/mach-at32ap/pio.c
+++ b/arch/avr32/mach-at32ap/pio.c
@@ -318,6 +318,8 @@
 		const char *label;
 
 		label = gpiochip_is_requested(chip, i);
+		if (!label && (imr & mask))
+			label = "[irq]";
 		if (!label)
 			continue;
 
diff --git a/arch/avr32/mach-at32ap/pio.h b/arch/avr32/mach-at32ap/pio.h
index 7795116..9484dfc 100644
--- a/arch/avr32/mach-at32ap/pio.h
+++ b/arch/avr32/mach-at32ap/pio.h
@@ -57,7 +57,7 @@
 
 /* Bitfields in IFDR */
 
-/* Bitfields in ISFR */
+/* Bitfields in IFSR */
 
 /* Bitfields in SODR */
 
diff --git a/arch/avr32/mach-at32ap/pm-at32ap700x.S b/arch/avr32/mach-at32ap/pm-at32ap700x.S
index 949e248..0a53ad3 100644
--- a/arch/avr32/mach-at32ap/pm-at32ap700x.S
+++ b/arch/avr32/mach-at32ap/pm-at32ap700x.S
@@ -12,6 +12,12 @@
 #include <asm/thread_info.h>
 #include <asm/arch/pm.h>
 
+#include "pm.h"
+#include "sdramc.h"
+
+/* Same as 0xfff00000 but fits in a 21 bit signed immediate */
+#define PM_BASE	-0x100000
+
 	.section .bss, "wa", @nobits
 	.global	disable_idle_sleep
 	.type	disable_idle_sleep, @object
@@ -64,3 +70,105 @@
 	unmask_interrupts
 	retal	r12
 	.size	cpu_idle_skip_sleep, . - cpu_idle_skip_sleep
+
+#ifdef CONFIG_PM
+	.section .init.text, "ax", @progbits
+
+	.global	pm_exception
+	.type	pm_exception, @function
+pm_exception:
+	/*
+	 * Exceptions are masked when we switch to this handler, so
+	 * we'll only get "unrecoverable" exceptions (offset 0.)
+	 */
+	sub	r12, pc, . - .Lpanic_msg
+	lddpc	pc, .Lpanic_addr
+
+	.align	2
+.Lpanic_addr:
+	.long	panic
+.Lpanic_msg:
+	.asciz	"Unrecoverable exception during suspend\n"
+	.size	pm_exception, . - pm_exception
+
+	.global	pm_irq0
+	.type	pm_irq0, @function
+pm_irq0:
+	/* Disable interrupts and return after the sleep instruction */
+	mfsr	r9, SYSREG_RSR_INT0
+	mtsr	SYSREG_RAR_INT0, r8
+	sbr	r9, SYSREG_GM_OFFSET
+	mtsr	SYSREG_RSR_INT0, r9
+	rete
+
+	/*
+	 * void cpu_enter_standby(unsigned long sdramc_base)
+	 *
+	 * Enter PM_SUSPEND_STANDBY mode. At this point, all drivers
+	 * are suspended and interrupts are disabled. Interrupts
+	 * marked as 'wakeup' event sources may still come along and
+	 * get us out of here.
+	 *
+	 * The SDRAM will be put into self-refresh mode (which does
+	 * not require a clock from the CPU), and the CPU will be put
+	 * into "frozen" mode (HSB bus stopped). The SDRAM controller
+	 * will automatically bring the SDRAM into normal mode on the
+	 * first access, and the power manager will automatically
+	 * start the HSB and CPU clocks upon a wakeup event.
+	 *
+	 * This code uses the same "skip sleep" technique as above.
+	 * It is very important that we jump directly to
+	 * cpu_after_sleep after the sleep instruction since that's
+	 * where we'll end up if the interrupt handler decides that we
+	 * need to skip the sleep instruction.
+	 */
+	.global	pm_standby
+	.type	pm_standby, @function
+pm_standby:
+	/*
+	 * interrupts are already masked at this point, and EVBA
+	 * points to pm_exception above.
+	 */
+	ld.w	r10, r12[SDRAMC_LPR]
+	sub	r8, pc, . - 1f		/* return address for irq handler */
+	mov	r11, SDRAMC_LPR_LPCB_SELF_RFR
+	bfins	r10, r11, 0, 2		/* LPCB <- self Refresh */
+	sync	0			/* flush write buffer */
+	st.w	r12[SDRAMC_LPR], r11	/* put SDRAM in self-refresh mode */
+	ld.w	r11, r12[SDRAMC_LPR]
+	unmask_interrupts
+	sleep	CPU_SLEEP_FROZEN
+1:	mask_interrupts
+	retal	r12
+	.size	pm_standby, . - pm_standby
+
+	.global	pm_suspend_to_ram
+	.type	pm_suspend_to_ram, @function
+pm_suspend_to_ram:
+	/*
+	 * interrupts are already masked at this point, and EVBA
+	 * points to pm_exception above.
+	 */
+	mov	r11, 0
+	cache	r11[2], 8		/* clean all dcache lines */
+	sync	0			/* flush write buffer */
+	ld.w	r10, r12[SDRAMC_LPR]
+	sub	r8, pc, . - 1f		/* return address for irq handler */
+	mov	r11, SDRAMC_LPR_LPCB_SELF_RFR
+	bfins	r10, r11, 0, 2		/* LPCB <- self refresh */
+	st.w	r12[SDRAMC_LPR], r10	/* put SDRAM in self-refresh mode */
+	ld.w	r11, r12[SDRAMC_LPR]
+
+	unmask_interrupts
+	sleep	CPU_SLEEP_STOP
+1:	mask_interrupts
+
+	retal	r12
+	.size	pm_suspend_to_ram, . - pm_suspend_to_ram
+
+	.global	pm_sram_end
+	.type	pm_sram_end, @function
+pm_sram_end:
+	.size	pm_sram_end, 0
+
+#endif /* CONFIG_PM */
diff --git a/arch/avr32/mach-at32ap/pm.c b/arch/avr32/mach-at32ap/pm.c
new file mode 100644
index 0000000..0b76432
--- /dev/null
+++ b/arch/avr32/mach-at32ap/pm.c
@@ -0,0 +1,245 @@
+/*
+ * AVR32 AP Power Management
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * 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/io.h>
+#include <linux/suspend.h>
+#include <linux/vmalloc.h>
+
+#include <asm/cacheflush.h>
+#include <asm/sysreg.h>
+
+#include <asm/arch/pm.h>
+#include <asm/arch/sram.h>
+
+/* FIXME: This is only valid for AP7000 */
+#define SDRAMC_BASE	0xfff03800
+
+#include "sdramc.h"
+
+#define SRAM_PAGE_FLAGS	(SYSREG_BIT(TLBELO_D) | SYSREG_BF(SZ, 1)	\
+				| SYSREG_BF(AP, 3) | SYSREG_BIT(G))
+
+
+static unsigned long	pm_sram_start;
+static size_t		pm_sram_size;
+static struct vm_struct	*pm_sram_area;
+
+static void (*avr32_pm_enter_standby)(unsigned long sdramc_base);
+static void (*avr32_pm_enter_str)(unsigned long sdramc_base);
+
+/*
+ * Must be called with interrupts disabled. Exceptions will be masked
+ * on return (i.e. all exceptions will be "unrecoverable".)
+ */
+static void *avr32_pm_map_sram(void)
+{
+	unsigned long	vaddr;
+	unsigned long	page_addr;
+	u32		tlbehi;
+	u32		mmucr;
+
+	vaddr = (unsigned long)pm_sram_area->addr;
+	page_addr = pm_sram_start & PAGE_MASK;
+
+	/*
+	 * Mask exceptions and grab the first TLB entry. We won't be
+	 * needing it while sleeping.
+	 */
+	asm volatile("ssrf	%0" : : "i"(SYSREG_EM_OFFSET) : "memory");
+
+	mmucr = sysreg_read(MMUCR);
+	tlbehi = sysreg_read(TLBEHI);
+	sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
+
+	tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
+	tlbehi |= vaddr & PAGE_MASK;
+	tlbehi |= SYSREG_BIT(TLBEHI_V);
+
+	sysreg_write(TLBELO, page_addr | SRAM_PAGE_FLAGS);
+	sysreg_write(TLBEHI, tlbehi);
+	__builtin_tlbw();
+
+	return (void *)(vaddr + pm_sram_start - page_addr);
+}
+
+/*
+ * Must be called with interrupts disabled. Exceptions will be
+ * unmasked on return.
+ */
+static void avr32_pm_unmap_sram(void)
+{
+	u32	mmucr;
+	u32	tlbehi;
+	u32	tlbarlo;
+
+	/* Going to update TLB entry at index 0 */
+	mmucr = sysreg_read(MMUCR);
+	tlbehi = sysreg_read(TLBEHI);
+	sysreg_write(MMUCR, SYSREG_BFINS(DRP, 0, mmucr));
+
+	/* Clear the "valid" bit */
+	tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
+	sysreg_write(TLBEHI, tlbehi);
+
+	/* Mark it as "not accessed" */
+	tlbarlo = sysreg_read(TLBARLO);
+	sysreg_write(TLBARLO, tlbarlo | 0x80000000U);
+
+	/* Update the TLB */
+	__builtin_tlbw();
+
+	/* Unmask exceptions */
+	asm volatile("csrf	%0" : : "i"(SYSREG_EM_OFFSET) : "memory");
+}
+
+static int avr32_pm_valid_state(suspend_state_t state)
+{
+	switch (state) {
+	case PM_SUSPEND_ON:
+	case PM_SUSPEND_STANDBY:
+	case PM_SUSPEND_MEM:
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+static int avr32_pm_enter(suspend_state_t state)
+{
+	u32		lpr_saved;
+	u32		evba_saved;
+	void		*sram;
+
+	switch (state) {
+	case PM_SUSPEND_STANDBY:
+		sram = avr32_pm_map_sram();
+
+		/* Switch to in-sram exception handlers */
+		evba_saved = sysreg_read(EVBA);
+		sysreg_write(EVBA, (unsigned long)sram);
+
+		/*
+		 * Save the LPR register so that we can re-enable
+		 * SDRAM Low Power mode on resume.
+		 */
+		lpr_saved = sdramc_readl(LPR);
+		pr_debug("%s: Entering standby...\n", __func__);
+		avr32_pm_enter_standby(SDRAMC_BASE);
+		sdramc_writel(LPR, lpr_saved);
+
+		/* Switch back to regular exception handlers */
+		sysreg_write(EVBA, evba_saved);
+
+		avr32_pm_unmap_sram();
+		break;
+
+	case PM_SUSPEND_MEM:
+		sram = avr32_pm_map_sram();
+
+		/* Switch to in-sram exception handlers */
+		evba_saved = sysreg_read(EVBA);
+		sysreg_write(EVBA, (unsigned long)sram);
+
+		/*
+		 * Save the LPR register so that we can re-enable
+		 * SDRAM Low Power mode on resume.
+		 */
+		lpr_saved = sdramc_readl(LPR);
+		pr_debug("%s: Entering suspend-to-ram...\n", __func__);
+		avr32_pm_enter_str(SDRAMC_BASE);
+		sdramc_writel(LPR, lpr_saved);
+
+		/* Switch back to regular exception handlers */
+		sysreg_write(EVBA, evba_saved);
+
+		avr32_pm_unmap_sram();
+		break;
+
+	case PM_SUSPEND_ON:
+		pr_debug("%s: Entering idle...\n", __func__);
+		cpu_enter_idle();
+		break;
+
+	default:
+		pr_debug("%s: Invalid suspend state %d\n", __func__, state);
+		goto out;
+	}
+
+	pr_debug("%s: wakeup\n", __func__);
+
+out:
+	return 0;
+}
+
+static struct platform_suspend_ops avr32_pm_ops = {
+	.valid	= avr32_pm_valid_state,
+	.enter	= avr32_pm_enter,
+};
+
+static unsigned long avr32_pm_offset(void *symbol)
+{
+	extern u8 pm_exception[];
+
+	return (unsigned long)symbol - (unsigned long)pm_exception;
+}
+
+static int __init avr32_pm_init(void)
+{
+	extern u8 pm_exception[];
+	extern u8 pm_irq0[];
+	extern u8 pm_standby[];
+	extern u8 pm_suspend_to_ram[];
+	extern u8 pm_sram_end[];
+	void *dst;
+
+	/*
+	 * To keep things simple, we depend on not needing more than a
+	 * single page.
+	 */
+	pm_sram_size = avr32_pm_offset(pm_sram_end);
+	if (pm_sram_size > PAGE_SIZE)
+		goto err;
+
+	pm_sram_start = sram_alloc(pm_sram_size);
+	if (!pm_sram_start)
+		goto err_alloc_sram;
+
+	/* Grab a virtual area we can use later on. */
+	pm_sram_area = get_vm_area(pm_sram_size, VM_IOREMAP);
+	if (!pm_sram_area)
+		goto err_vm_area;
+	pm_sram_area->phys_addr = pm_sram_start;
+
+	local_irq_disable();
+	dst = avr32_pm_map_sram();
+	memcpy(dst, pm_exception, pm_sram_size);
+	flush_dcache_region(dst, pm_sram_size);
+	invalidate_icache_region(dst, pm_sram_size);
+	avr32_pm_unmap_sram();
+	local_irq_enable();
+
+	avr32_pm_enter_standby = dst + avr32_pm_offset(pm_standby);
+	avr32_pm_enter_str = dst + avr32_pm_offset(pm_suspend_to_ram);
+	intc_set_suspend_handler(avr32_pm_offset(pm_irq0));
+
+	suspend_set_ops(&avr32_pm_ops);
+
+	printk("AVR32 AP Power Management enabled\n");
+
+	return 0;
+
+err_vm_area:
+	sram_free(pm_sram_start, pm_sram_size);
+err_alloc_sram:
+err:
+	pr_err("AVR32 Power Management initialization failed\n");
+	return -ENOMEM;
+}
+arch_initcall(avr32_pm_init);
diff --git a/arch/avr32/mach-at32ap/sdramc.h b/arch/avr32/mach-at32ap/sdramc.h
new file mode 100644
index 0000000..66eeaed
--- /dev/null
+++ b/arch/avr32/mach-at32ap/sdramc.h
@@ -0,0 +1,76 @@
+/*
+ * Register definitions for the AT32AP SDRAM Controller
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * 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.
+ */
+
+/* Register offsets */
+#define SDRAMC_MR			0x0000
+#define SDRAMC_TR			0x0004
+#define SDRAMC_CR			0x0008
+#define SDRAMC_HSR			0x000c
+#define SDRAMC_LPR			0x0010
+#define SDRAMC_IER			0x0014
+#define SDRAMC_IDR			0x0018
+#define SDRAMC_IMR			0x001c
+#define SDRAMC_ISR			0x0020
+#define SDRAMC_MDR			0x0024
+
+/* MR - Mode Register */
+#define SDRAMC_MR_MODE_NORMAL		(  0 <<  0)
+#define SDRAMC_MR_MODE_NOP		(  1 <<  0)
+#define SDRAMC_MR_MODE_BANKS_PRECHARGE	(  2 <<  0)
+#define SDRAMC_MR_MODE_LOAD_MODE	(  3 <<  0)
+#define SDRAMC_MR_MODE_AUTO_REFRESH	(  4 <<  0)
+#define SDRAMC_MR_MODE_EXT_LOAD_MODE	(  5 <<  0)
+#define SDRAMC_MR_MODE_POWER_DOWN	(  6 <<  0)
+
+/* CR - Configuration Register */
+#define SDRAMC_CR_NC_8_BITS		(  0 <<  0)
+#define SDRAMC_CR_NC_9_BITS		(  1 <<  0)
+#define SDRAMC_CR_NC_10_BITS		(  2 <<  0)
+#define SDRAMC_CR_NC_11_BITS		(  3 <<  0)
+#define SDRAMC_CR_NR_11_BITS		(  0 <<  2)
+#define SDRAMC_CR_NR_12_BITS		(  1 <<  2)
+#define SDRAMC_CR_NR_13_BITS		(  2 <<  2)
+#define SDRAMC_CR_NB_2_BANKS		(  0 <<  4)
+#define SDRAMC_CR_NB_4_BANKS		(  1 <<  4)
+#define SDRAMC_CR_CAS(x)		((x) <<  5)
+#define SDRAMC_CR_DBW_32_BITS		(  0 <<  7)
+#define SDRAMC_CR_DBW_16_BITS		(  1 <<  7)
+#define SDRAMC_CR_TWR(x)		((x) <<  8)
+#define SDRAMC_CR_TRC(x)		((x) << 12)
+#define SDRAMC_CR_TRP(x)		((x) << 16)
+#define SDRAMC_CR_TRCD(x)		((x) << 20)
+#define SDRAMC_CR_TRAS(x)		((x) << 24)
+#define SDRAMC_CR_TXSR(x)		((x) << 28)
+
+/* HSR - High Speed Register */
+#define SDRAMC_HSR_DA			(  1 <<  0)
+
+/* LPR - Low Power Register */
+#define SDRAMC_LPR_LPCB_INHIBIT		(  0 <<  0)
+#define SDRAMC_LPR_LPCB_SELF_RFR	(  1 <<  0)
+#define SDRAMC_LPR_LPCB_PDOWN		(  2 <<  0)
+#define SDRAMC_LPR_LPCB_DEEP_PDOWN	(  3 <<  0)
+#define SDRAMC_LPR_PASR(x)		((x) <<  4)
+#define SDRAMC_LPR_TCSR(x)		((x) <<  8)
+#define SDRAMC_LPR_DS(x)		((x) << 10)
+#define SDRAMC_LPR_TIMEOUT(x)		((x) << 12)
+
+/* IER/IDR/IMR/ISR - Interrupt Enable/Disable/Mask/Status Register */
+#define SDRAMC_ISR_RES			(  1 <<  0)
+
+/* MDR - Memory Device Register */
+#define SDRAMC_MDR_MD_SDRAM		(  0 <<  0)
+#define SDRAMC_MDR_MD_LOW_PWR_SDRAM	(  1 <<  0)
+
+/* Register access macros */
+#define sdramc_readl(reg) \
+	__raw_readl((void __iomem __force *)SDRAMC_BASE + SDRAMC_##reg)
+#define sdramc_writel(reg, value) \
+	__raw_writel(value, (void __iomem __force *)SDRAMC_BASE + SDRAMC_##reg)
diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c
index 0e64ddc..3f90a87 100644
--- a/arch/avr32/mm/init.c
+++ b/arch/avr32/mm/init.c
@@ -11,6 +11,7 @@
 #include <linux/swap.h>
 #include <linux/init.h>
 #include <linux/mmzone.h>
+#include <linux/module.h>
 #include <linux/bootmem.h>
 #include <linux/pagemap.h>
 #include <linux/nodemask.h>
@@ -23,11 +24,14 @@
 #include <asm/setup.h>
 #include <asm/sections.h>
 
+#define __page_aligned	__attribute__((section(".data.page_aligned")))
+
 DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 
-pgd_t swapper_pg_dir[PTRS_PER_PGD];
+pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned;
 
 struct page *empty_zero_page;
+EXPORT_SYMBOL(empty_zero_page);
 
 /*
  * Cache of MMU context last used.
@@ -106,19 +110,9 @@
 	zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0),
 						 PAGE_SIZE);
 
-	{
-		pgd_t *pg_dir;
-		int i;
-
-		pg_dir = swapper_pg_dir;
-		sysreg_write(PTBR, (unsigned long)pg_dir);
-
-		for (i = 0; i < PTRS_PER_PGD; i++)
-			pgd_val(pg_dir[i]) = 0;
-
-		enable_mmu();
-		printk ("CPU: Paging enabled\n");
-	}
+	sysreg_write(PTBR, (unsigned long)swapper_pg_dir);
+	enable_mmu();
+	printk ("CPU: Paging enabled\n");
 
 	for_each_online_node(nid) {
 		pg_data_t *pgdat = NODE_DATA(nid);
diff --git a/arch/avr32/mm/tlb.c b/arch/avr32/mm/tlb.c
index cd12edb..06677be 100644
--- a/arch/avr32/mm/tlb.c
+++ b/arch/avr32/mm/tlb.c
@@ -11,21 +11,21 @@
 
 #include <asm/mmu_context.h>
 
-#define _TLBEHI_I	0x100
+/* TODO: Get the correct number from the CONFIG1 system register */
+#define NR_TLB_ENTRIES 32
 
-void show_dtlb_entry(unsigned int index)
+static void show_dtlb_entry(unsigned int index)
 {
-	unsigned int tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
+	u32 tlbehi, tlbehi_save, tlbelo, mmucr, mmucr_save;
 	unsigned long flags;
 
 	local_irq_save(flags);
 	mmucr_save = sysreg_read(MMUCR);
 	tlbehi_save = sysreg_read(TLBEHI);
-	mmucr = mmucr_save & 0x13;
-	mmucr |= index << 14;
+	mmucr = SYSREG_BFINS(DRP, index, mmucr_save);
 	sysreg_write(MMUCR, mmucr);
 
-	asm volatile("tlbr" : : : "memory");
+	__builtin_tlbr();
 	cpu_sync_pipeline();
 
 	tlbehi = sysreg_read(TLBEHI);
@@ -33,15 +33,17 @@
 
 	printk("%2u: %c %c %02x   %05x %05x %o  %o  %c %c %c %c\n",
 	       index,
-	       (tlbehi & 0x200)?'1':'0',
-	       (tlbelo & 0x100)?'1':'0',
-	       (tlbehi & 0xff),
-	       (tlbehi >> 12), (tlbelo >> 12),
-	       (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
-	       (tlbelo & 0x200)?'1':'0',
-	       (tlbelo & 0x080)?'1':'0',
-	       (tlbelo & 0x001)?'1':'0',
-	       (tlbelo & 0x002)?'1':'0');
+	       SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0',
+	       SYSREG_BFEXT(G, tlbelo) ? '1' : '0',
+	       SYSREG_BFEXT(ASID, tlbehi),
+	       SYSREG_BFEXT(VPN, tlbehi) >> 2,
+	       SYSREG_BFEXT(PFN, tlbelo) >> 2,
+	       SYSREG_BFEXT(AP, tlbelo),
+	       SYSREG_BFEXT(SZ, tlbelo),
+	       SYSREG_BFEXT(TLBELO_C, tlbelo) ? 'C' : ' ',
+	       SYSREG_BFEXT(B, tlbelo) ? 'B' : ' ',
+	       SYSREG_BFEXT(W, tlbelo) ? 'W' : ' ',
+	       SYSREG_BFEXT(TLBELO_D, tlbelo) ? 'D' : ' ');
 
 	sysreg_write(MMUCR, mmucr_save);
 	sysreg_write(TLBEHI, tlbehi_save);
@@ -54,29 +56,33 @@
 	unsigned int i;
 
 	printk("ID  V G ASID VPN   PFN   AP SZ C B W D\n");
-	for (i = 0; i < 32; i++)
+	for (i = 0; i < NR_TLB_ENTRIES; i++)
 		show_dtlb_entry(i);
 }
 
-static unsigned long last_mmucr;
-
-static inline void set_replacement_pointer(unsigned shift)
+static void update_dtlb(unsigned long address, pte_t pte)
 {
-	unsigned long mmucr, mmucr_save;
+	u32 tlbehi;
+	u32 mmucr;
 
-	mmucr = mmucr_save = sysreg_read(MMUCR);
+	/*
+	 * We're not changing the ASID here, so no need to flush the
+	 * pipeline.
+	 */
+	tlbehi = sysreg_read(TLBEHI);
+	tlbehi = SYSREG_BF(ASID, SYSREG_BFEXT(ASID, tlbehi));
+	tlbehi |= address & MMU_VPN_MASK;
+	tlbehi |= SYSREG_BIT(TLBEHI_V);
+	sysreg_write(TLBEHI, tlbehi);
 
 	/* Does this mapping already exist? */
-	__asm__ __volatile__(
-		"	tlbs\n"
-		"	mfsr %0, %1"
-		: "=r"(mmucr)
-		: "i"(SYSREG_MMUCR));
+	__builtin_tlbs();
+	mmucr = sysreg_read(MMUCR);
 
 	if (mmucr & SYSREG_BIT(MMUCR_N)) {
 		/* Not found -- pick a not-recently-accessed entry */
-		unsigned long rp;
-		unsigned long tlbar = sysreg_read(TLBARLO);
+		unsigned int rp;
+		u32 tlbar = sysreg_read(TLBARLO);
 
 		rp = 32 - fls(tlbar);
 		if (rp == 32) {
@@ -84,30 +90,14 @@
 			sysreg_write(TLBARLO, -1L);
 		}
 
-		mmucr &= 0x13;
-		mmucr |= (rp << shift);
-
+		mmucr = SYSREG_BFINS(DRP, rp, mmucr);
 		sysreg_write(MMUCR, mmucr);
 	}
 
-	last_mmucr = mmucr;
-}
-
-static void update_dtlb(unsigned long address, pte_t pte, unsigned long asid)
-{
-	unsigned long vpn;
-
-	vpn = (address & MMU_VPN_MASK) | _TLBEHI_VALID | asid;
-	sysreg_write(TLBEHI, vpn);
-	cpu_sync_pipeline();
-
-	set_replacement_pointer(14);
-
 	sysreg_write(TLBELO, pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK);
 
 	/* Let's go */
-	asm volatile("nop\n\ttlbw" : : : "memory");
-	cpu_sync_pipeline();
+	__builtin_tlbw();
 }
 
 void update_mmu_cache(struct vm_area_struct *vma,
@@ -120,39 +110,40 @@
 		return;
 
 	local_irq_save(flags);
-	update_dtlb(address, pte, get_asid());
+	update_dtlb(address, pte);
 	local_irq_restore(flags);
 }
 
-void __flush_tlb_page(unsigned long asid, unsigned long page)
+static void __flush_tlb_page(unsigned long asid, unsigned long page)
 {
-	unsigned long mmucr, tlbehi;
+	u32 mmucr, tlbehi;
 
-	page |= asid;
-	sysreg_write(TLBEHI, page);
-	cpu_sync_pipeline();
-	asm volatile("tlbs");
+	/*
+	 * Caller is responsible for masking out non-PFN bits in page
+	 * and changing the current ASID if necessary. This means that
+	 * we don't need to flush the pipeline after writing TLBEHI.
+	 */
+	tlbehi = page | asid;
+	sysreg_write(TLBEHI, tlbehi);
+
+	__builtin_tlbs();
 	mmucr = sysreg_read(MMUCR);
 
 	if (!(mmucr & SYSREG_BIT(MMUCR_N))) {
-		unsigned long tlbarlo;
-		unsigned long entry;
+		unsigned int entry;
+		u32 tlbarlo;
 
 		/* Clear the "valid" bit */
-		tlbehi = sysreg_read(TLBEHI);
-		tlbehi &= ~_TLBEHI_VALID;
 		sysreg_write(TLBEHI, tlbehi);
-		cpu_sync_pipeline();
 
 		/* mark the entry as "not accessed" */
-		entry = (mmucr >> 14) & 0x3f;
+		entry = SYSREG_BFEXT(DRP, mmucr);
 		tlbarlo = sysreg_read(TLBARLO);
-		tlbarlo |= (0x80000000 >> entry);
+		tlbarlo |= (0x80000000UL >> entry);
 		sysreg_write(TLBARLO, tlbarlo);
 
 		/* update the entry with valid bit clear */
-		asm volatile("tlbw");
-		cpu_sync_pipeline();
+		__builtin_tlbw();
 	}
 }
 
@@ -190,17 +181,22 @@
 
 		local_irq_save(flags);
 		size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
 		if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
 			mm->context = NO_CONTEXT;
 			if (mm == current->mm)
 				activate_context(mm);
 		} else {
-			unsigned long asid = mm->context & MMU_CONTEXT_ASID_MASK;
-			unsigned long saved_asid = MMU_NO_ASID;
+			unsigned long asid;
+			unsigned long saved_asid;
+
+			asid = mm->context & MMU_CONTEXT_ASID_MASK;
+			saved_asid = MMU_NO_ASID;
 
 			start &= PAGE_MASK;
 			end += (PAGE_SIZE - 1);
 			end &= PAGE_MASK;
+
 			if (mm != current->mm) {
 				saved_asid = get_asid();
 				set_asid(asid);
@@ -218,33 +214,34 @@
 }
 
 /*
- * TODO: If this is only called for addresses > TASK_SIZE, we can probably
- * skip the ASID stuff and just use the Global bit...
+ * This function depends on the pages to be flushed having the G
+ * (global) bit set in their pte. This is true for all
+ * PAGE_KERNEL(_RO) pages.
  */
 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 {
 	unsigned long flags;
 	int size;
 
-	local_irq_save(flags);
 	size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 	if (size > (MMU_DTLB_ENTRIES / 4)) { /* Too many entries to flush */
 		flush_tlb_all();
 	} else {
-		unsigned long asid = init_mm.context & MMU_CONTEXT_ASID_MASK;
-		unsigned long saved_asid = get_asid();
+		unsigned long asid;
+
+		local_irq_save(flags);
+		asid = get_asid();
 
 		start &= PAGE_MASK;
 		end += (PAGE_SIZE - 1);
 		end &= PAGE_MASK;
-		set_asid(asid);
+
 		while (start < end) {
 			__flush_tlb_page(asid, start);
 			start += PAGE_SIZE;
 		}
-		set_asid(saved_asid);
+		local_irq_restore(flags);
 	}
-	local_irq_restore(flags);
 }
 
 void flush_tlb_mm(struct mm_struct *mm)
@@ -280,7 +277,7 @@
 {
 	static unsigned long tlb_index;
 
-	if (*pos >= 32)
+	if (*pos >= NR_TLB_ENTRIES)
 		return NULL;
 
 	tlb_index = 0;
@@ -291,7 +288,7 @@
 {
 	unsigned long *index = v;
 
-	if (*index >= 31)
+	if (*index >= NR_TLB_ENTRIES - 1)
 		return NULL;
 
 	++*pos;
@@ -313,16 +310,16 @@
 	if (*index == 0)
 		seq_puts(tlb, "ID  V G ASID VPN   PFN   AP SZ C B W D\n");
 
-	BUG_ON(*index >= 32);
+	BUG_ON(*index >= NR_TLB_ENTRIES);
 
 	local_irq_save(flags);
 	mmucr_save = sysreg_read(MMUCR);
 	tlbehi_save = sysreg_read(TLBEHI);
-	mmucr = mmucr_save & 0x13;
-	mmucr |= *index << 14;
+	mmucr = SYSREG_BFINS(DRP, *index, mmucr_save);
 	sysreg_write(MMUCR, mmucr);
 
-	asm volatile("tlbr" : : : "memory");
+	/* TLBR might change the ASID */
+	__builtin_tlbr();
 	cpu_sync_pipeline();
 
 	tlbehi = sysreg_read(TLBEHI);
@@ -334,16 +331,18 @@
 	local_irq_restore(flags);
 
 	seq_printf(tlb, "%2lu: %c %c %02x   %05x %05x %o  %o  %c %c %c %c\n",
-	       *index,
-	       (tlbehi & 0x200)?'1':'0',
-	       (tlbelo & 0x100)?'1':'0',
-	       (tlbehi & 0xff),
-	       (tlbehi >> 12), (tlbelo >> 12),
-	       (tlbelo >> 4) & 7, (tlbelo >> 2) & 3,
-	       (tlbelo & 0x200)?'1':'0',
-	       (tlbelo & 0x080)?'1':'0',
-	       (tlbelo & 0x001)?'1':'0',
-	       (tlbelo & 0x002)?'1':'0');
+		   *index,
+		   SYSREG_BFEXT(TLBEHI_V, tlbehi) ? '1' : '0',
+		   SYSREG_BFEXT(G, tlbelo) ? '1' : '0',
+		   SYSREG_BFEXT(ASID, tlbehi),
+		   SYSREG_BFEXT(VPN, tlbehi) >> 2,
+		   SYSREG_BFEXT(PFN, tlbelo) >> 2,
+		   SYSREG_BFEXT(AP, tlbelo),
+		   SYSREG_BFEXT(SZ, tlbelo),
+		   SYSREG_BFEXT(TLBELO_C, tlbelo) ? '1' : '0',
+		   SYSREG_BFEXT(B, tlbelo) ? '1' : '0',
+		   SYSREG_BFEXT(W, tlbelo) ? '1' : '0',
+		   SYSREG_BFEXT(TLBELO_D, tlbelo) ? '1' : '0');
 
 	return 0;
 }
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 107e492..5dc8f80 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -146,6 +146,7 @@
 config COMPAT
 	bool "Kernel support for 31 bit emulation"
 	depends on 64BIT
+	select COMPAT_BINFMT_ELF
 	help
 	  Select this option if you want to enable your system kernel to
 	  handle system-calls from ELF binaries for 31 bit ESA.  This option
@@ -312,6 +313,10 @@
 config ARCH_SELECT_MEMORY_MODEL
        def_bool y
 
+config ARCH_ENABLE_MEMORY_HOTPLUG
+	def_bool y
+	depends on SPARSEMEM
+
 source "mm/Kconfig"
 
 comment "I/O subsystem configuration"
@@ -344,6 +349,22 @@
 
 	  If unsure, say N.
 
+config CHSC_SCH
+	tristate "Support for CHSC subchannels"
+	help
+	  This driver allows usage of CHSC subchannels. A CHSC subchannel
+	  is usually present on LPAR only.
+	  The driver creates a device /dev/chsc, which may be used to
+	  obtain I/O configuration information about the machine and
+	  to issue asynchronous chsc commands (DANGEROUS).
+	  You will usually only want to use this interface on a special
+	  LPAR designated for system management.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called chsc_sch.
+
+	  If unsure, say N.
+
 comment "Misc"
 
 config IPL
diff --git a/arch/s390/appldata/appldata.h b/arch/s390/appldata/appldata.h
index db3ae85..17a2636 100644
--- a/arch/s390/appldata/appldata.h
+++ b/arch/s390/appldata/appldata.h
@@ -3,13 +3,11 @@
  *
  * Definitions and interface for Linux - z/VM Monitor Stream.
  *
- * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH.
+ * Copyright IBM Corp. 2003, 2008
  *
  * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
  */
 
-//#define APPLDATA_DEBUG			/* Debug messages on/off */
-
 #define APPLDATA_MAX_REC_SIZE	  4024	/* Maximum size of the */
 					/* data buffer */
 #define APPLDATA_MAX_PROCS 100
@@ -32,12 +30,6 @@
 #define P_ERROR(x...)	printk(KERN_ERR MY_PRINT_NAME " error: " x)
 #define P_WARNING(x...)	printk(KERN_WARNING MY_PRINT_NAME " status: " x)
 
-#ifdef APPLDATA_DEBUG
-#define P_DEBUG(x...)   printk(KERN_DEBUG MY_PRINT_NAME " debug: " x)
-#else
-#define P_DEBUG(x...)   do {} while (0)
-#endif
-
 struct appldata_ops {
 	struct list_head list;
 	struct ctl_table_header *sysctl_header;
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c
index ad40729..9cb3d92 100644
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -5,7 +5,7 @@
  * Exports appldata_register_ops() and appldata_unregister_ops() for the
  * data gathering modules.
  *
- * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH.
+ * Copyright IBM Corp. 2003, 2008
  *
  * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
  */
@@ -108,9 +108,6 @@
  */
 static void appldata_timer_function(unsigned long data)
 {
-	P_DEBUG("   -= Timer =-\n");
-	P_DEBUG("CPU: %i, expire_count: %i\n", smp_processor_id(),
-		atomic_read(&appldata_expire_count));
 	if (atomic_dec_and_test(&appldata_expire_count)) {
 		atomic_set(&appldata_expire_count, num_online_cpus());
 		queue_work(appldata_wq, (struct work_struct *) data);
@@ -128,14 +125,11 @@
 	struct appldata_ops *ops;
 	int i;
 
-	P_DEBUG("  -= Work Queue =-\n");
 	i = 0;
 	get_online_cpus();
 	spin_lock(&appldata_ops_lock);
 	list_for_each(lh, &appldata_ops_list) {
 		ops = list_entry(lh, struct appldata_ops, list);
-		P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n",
-			++i, ops->active, ops->name);
 		if (ops->active == 1) {
 			ops->callback(ops->data);
 		}
@@ -212,7 +206,6 @@
 						 0, 1);
 		}
 		appldata_timer_active = 1;
-		P_INFO("Monitoring timer started.\n");
 		break;
 	case APPLDATA_DEL_TIMER:
 		for_each_online_cpu(i)
@@ -221,7 +214,6 @@
 			break;
 		appldata_timer_active = 0;
 		atomic_set(&appldata_expire_count, num_online_cpus());
-		P_INFO("Monitoring timer stopped.\n");
 		break;
 	case APPLDATA_MOD_TIMER:
 		per_cpu_interval = (u64) (appldata_interval*1000 /
@@ -313,10 +305,8 @@
 	}
 	interval = 0;
 	sscanf(buf, "%i", &interval);
-	if (interval <= 0) {
-		P_ERROR("Timer CPU interval has to be > 0!\n");
+	if (interval <= 0)
 		return -EINVAL;
-	}
 
 	get_online_cpus();
 	spin_lock(&appldata_timer_lock);
@@ -324,9 +314,6 @@
 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
 	spin_unlock(&appldata_timer_lock);
 	put_online_cpus();
-
-	P_INFO("Monitoring CPU interval set to %u milliseconds.\n",
-		 interval);
 out:
 	*lenp = len;
 	*ppos += len;
@@ -406,23 +393,16 @@
 			P_ERROR("START DIAG 0xDC for %s failed, "
 				"return code: %d\n", ops->name, rc);
 			module_put(ops->owner);
-		} else {
-			P_INFO("Monitoring %s data enabled, "
-				"DIAG 0xDC started.\n", ops->name);
+		} else
 			ops->active = 1;
-		}
 	} else if ((buf[0] == '0') && (ops->active == 1)) {
 		ops->active = 0;
 		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
 				(unsigned long) ops->data, ops->size,
 				ops->mod_lvl);
-		if (rc != 0) {
+		if (rc != 0)
 			P_ERROR("STOP DIAG 0xDC for %s failed, "
 				"return code: %d\n", ops->name, rc);
-		} else {
-			P_INFO("Monitoring %s data disabled, "
-				"DIAG 0xDC stopped.\n", ops->name);
-		}
 		module_put(ops->owner);
 	}
 	spin_unlock(&appldata_ops_lock);
@@ -468,7 +448,6 @@
 	ops->sysctl_header = register_sysctl_table(ops->ctl_table);
 	if (!ops->sysctl_header)
 		goto out;
-	P_INFO("%s-ops registered!\n", ops->name);
 	return 0;
 out:
 	spin_lock(&appldata_ops_lock);
@@ -490,7 +469,6 @@
 	spin_unlock(&appldata_ops_lock);
 	unregister_sysctl_table(ops->sysctl_header);
 	kfree(ops->ctl_table);
-	P_INFO("%s-ops unregistered!\n", ops->name);
 }
 /********************** module-ops management <END> **************************/
 
@@ -553,14 +531,9 @@
 {
 	int i;
 
-	P_DEBUG("sizeof(parameter_list) = %lu\n",
-		sizeof(struct appldata_parameter_list));
-
 	appldata_wq = create_singlethread_workqueue("appldata");
-	if (!appldata_wq) {
-		P_ERROR("Could not create work queue\n");
+	if (!appldata_wq)
 		return -ENOMEM;
-	}
 
 	get_online_cpus();
 	for_each_online_cpu(i)
@@ -571,8 +544,6 @@
 	register_hotcpu_notifier(&appldata_nb);
 
 	appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
-
-	P_DEBUG("Base interface initialized.\n");
 	return 0;
 }
 
@@ -584,7 +555,9 @@
 EXPORT_SYMBOL_GPL(appldata_unregister_ops);
 EXPORT_SYMBOL_GPL(appldata_diag);
 
+#ifdef CONFIG_SWAP
 EXPORT_SYMBOL_GPL(si_swapinfo);
+#endif
 EXPORT_SYMBOL_GPL(nr_threads);
 EXPORT_SYMBOL_GPL(nr_running);
 EXPORT_SYMBOL_GPL(nr_iowait);
diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c
index 51181cc..3ed56b7 100644
--- a/arch/s390/appldata/appldata_mem.c
+++ b/arch/s390/appldata/appldata_mem.c
@@ -14,14 +14,13 @@
 #include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
-#include <asm/io.h>
 #include <linux/pagemap.h>
 #include <linux/swap.h>
+#include <asm/io.h>
 
 #include "appldata.h"
 
 
-#define MY_PRINT_NAME "appldata_mem"		/* for debug messages, etc. */
 #define P2K(x) ((x) << (PAGE_SHIFT - 10))	/* Converts #Pages to KB */
 
 /*
@@ -70,30 +69,6 @@
 } __attribute__((packed)) appldata_mem_data;
 
 
-static inline void appldata_debug_print(struct appldata_mem_data *mem_data)
-{
-	P_DEBUG("--- MEM - RECORD ---\n");
-	P_DEBUG("pgpgin     = %8lu KB\n", mem_data->pgpgin);
-	P_DEBUG("pgpgout    = %8lu KB\n", mem_data->pgpgout);
-	P_DEBUG("pswpin     = %8lu Pages\n", mem_data->pswpin);
-	P_DEBUG("pswpout    = %8lu Pages\n", mem_data->pswpout);
-	P_DEBUG("pgalloc    = %8lu \n", mem_data->pgalloc);
-	P_DEBUG("pgfault    = %8lu \n", mem_data->pgfault);
-	P_DEBUG("pgmajfault = %8lu \n", mem_data->pgmajfault);
-	P_DEBUG("sharedram  = %8lu KB\n", mem_data->sharedram);
-	P_DEBUG("totalram   = %8lu KB\n", mem_data->totalram);
-	P_DEBUG("freeram    = %8lu KB\n", mem_data->freeram);
-	P_DEBUG("totalhigh  = %8lu KB\n", mem_data->totalhigh);
-	P_DEBUG("freehigh   = %8lu KB\n", mem_data->freehigh);
-	P_DEBUG("bufferram  = %8lu KB\n", mem_data->bufferram);
-	P_DEBUG("cached     = %8lu KB\n", mem_data->cached);
-	P_DEBUG("totalswap  = %8lu KB\n", mem_data->totalswap);
-	P_DEBUG("freeswap   = %8lu KB\n", mem_data->freeswap);
-	P_DEBUG("sync_count_1 = %u\n", mem_data->sync_count_1);
-	P_DEBUG("sync_count_2 = %u\n", mem_data->sync_count_2);
-	P_DEBUG("timestamp    = %lX\n", mem_data->timestamp);
-}
-
 /*
  * appldata_get_mem_data()
  *
@@ -140,9 +115,6 @@
 
 	mem_data->timestamp = get_clock();
 	mem_data->sync_count_2++;
-#ifdef APPLDATA_DEBUG
-	appldata_debug_print(mem_data);
-#endif
 }
 
 
@@ -164,17 +136,7 @@
  */
 static int __init appldata_mem_init(void)
 {
-	int rc;
-
-	P_DEBUG("sizeof(mem) = %lu\n", sizeof(struct appldata_mem_data));
-
-	rc = appldata_register_ops(&ops);
-	if (rc != 0) {
-		P_ERROR("Error registering ops, rc = %i\n", rc);
-	} else {
-		P_DEBUG("%s-ops registered!\n", ops.name);
-	}
-	return rc;
+	return appldata_register_ops(&ops);
 }
 
 /*
@@ -185,7 +147,6 @@
 static void __exit appldata_mem_exit(void)
 {
 	appldata_unregister_ops(&ops);
-	P_DEBUG("%s-ops unregistered!\n", ops.name);
 }
 
 
diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c
index 4d83443..3b74655 100644
--- a/arch/s390/appldata/appldata_net_sum.c
+++ b/arch/s390/appldata/appldata_net_sum.c
@@ -21,9 +21,6 @@
 #include "appldata.h"
 
 
-#define MY_PRINT_NAME	"appldata_net_sum"	/* for debug messages, etc. */
-
-
 /*
  * Network data
  *
@@ -60,26 +57,6 @@
 } __attribute__((packed)) appldata_net_sum_data;
 
 
-static inline void appldata_print_debug(struct appldata_net_sum_data *net_data)
-{
-	P_DEBUG("--- NET - RECORD ---\n");
-
-	P_DEBUG("nr_interfaces = %u\n", net_data->nr_interfaces);
-	P_DEBUG("rx_packets    = %8lu\n", net_data->rx_packets);
-	P_DEBUG("tx_packets    = %8lu\n", net_data->tx_packets);
-	P_DEBUG("rx_bytes      = %8lu\n", net_data->rx_bytes);
-	P_DEBUG("tx_bytes      = %8lu\n", net_data->tx_bytes);
-	P_DEBUG("rx_errors     = %8lu\n", net_data->rx_errors);
-	P_DEBUG("tx_errors     = %8lu\n", net_data->tx_errors);
-	P_DEBUG("rx_dropped    = %8lu\n", net_data->rx_dropped);
-	P_DEBUG("tx_dropped    = %8lu\n", net_data->tx_dropped);
-	P_DEBUG("collisions    = %8lu\n", net_data->collisions);
-
-	P_DEBUG("sync_count_1 = %u\n", net_data->sync_count_1);
-	P_DEBUG("sync_count_2 = %u\n", net_data->sync_count_2);
-	P_DEBUG("timestamp    = %lX\n", net_data->timestamp);
-}
-
 /*
  * appldata_get_net_sum_data()
  *
@@ -135,9 +112,6 @@
 
 	net_data->timestamp = get_clock();
 	net_data->sync_count_2++;
-#ifdef APPLDATA_DEBUG
-	appldata_print_debug(net_data);
-#endif
 }
 
 
@@ -159,17 +133,7 @@
  */
 static int __init appldata_net_init(void)
 {
-	int rc;
-
-	P_DEBUG("sizeof(net) = %lu\n", sizeof(struct appldata_net_sum_data));
-
-	rc = appldata_register_ops(&ops);
-	if (rc != 0) {
-		P_ERROR("Error registering ops, rc = %i\n", rc);
-	} else {
-		P_DEBUG("%s-ops registered!\n", ops.name);
-	}
-	return rc;
+	return appldata_register_ops(&ops);
 }
 
 /*
@@ -180,7 +144,6 @@
 static void __exit appldata_net_exit(void)
 {
 	appldata_unregister_ops(&ops);
-	P_DEBUG("%s-ops unregistered!\n", ops.name);
 }
 
 
diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c
index 6b3eafe..eb44f9f 100644
--- a/arch/s390/appldata/appldata_os.c
+++ b/arch/s390/appldata/appldata_os.c
@@ -89,44 +89,6 @@
 };
 
 
-static inline void appldata_print_debug(struct appldata_os_data *os_data)
-{
-	int a0, a1, a2, i;
-
-	P_DEBUG("--- OS - RECORD ---\n");
-	P_DEBUG("nr_threads   = %u\n", os_data->nr_threads);
-	P_DEBUG("nr_running   = %u\n", os_data->nr_running);
-	P_DEBUG("nr_iowait    = %u\n", os_data->nr_iowait);
-	P_DEBUG("avenrun(int) = %8x / %8x / %8x\n", os_data->avenrun[0],
-		os_data->avenrun[1], os_data->avenrun[2]);
-	a0 = os_data->avenrun[0];
-	a1 = os_data->avenrun[1];
-	a2 = os_data->avenrun[2];
-	P_DEBUG("avenrun(float) = %d.%02d / %d.%02d / %d.%02d\n",
-		LOAD_INT(a0), LOAD_FRAC(a0), LOAD_INT(a1), LOAD_FRAC(a1),
-		LOAD_INT(a2), LOAD_FRAC(a2));
-
-	P_DEBUG("nr_cpus = %u\n", os_data->nr_cpus);
-	for (i = 0; i < os_data->nr_cpus; i++) {
-		P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, "
-			"idle = %u, irq = %u, softirq = %u, iowait = %u, "
-			"steal = %u\n",
-				os_data->os_cpu[i].cpu_id,
-				os_data->os_cpu[i].per_cpu_user,
-				os_data->os_cpu[i].per_cpu_nice,
-				os_data->os_cpu[i].per_cpu_system,
-				os_data->os_cpu[i].per_cpu_idle,
-				os_data->os_cpu[i].per_cpu_irq,
-				os_data->os_cpu[i].per_cpu_softirq,
-				os_data->os_cpu[i].per_cpu_iowait,
-				os_data->os_cpu[i].per_cpu_steal);
-	}
-
-	P_DEBUG("sync_count_1 = %u\n", os_data->sync_count_1);
-	P_DEBUG("sync_count_2 = %u\n", os_data->sync_count_2);
-	P_DEBUG("timestamp    = %lX\n", os_data->timestamp);
-}
-
 /*
  * appldata_get_os_data()
  *
@@ -180,13 +142,10 @@
 					   APPLDATA_START_INTERVAL_REC,
 					   (unsigned long) ops.data, new_size,
 					   ops.mod_lvl);
-			if (rc != 0) {
+			if (rc != 0)
 				P_ERROR("os: START NEW DIAG 0xDC failed, "
 					"return code: %d, new size = %i\n", rc,
 					new_size);
-				P_INFO("os: stopping old record now\n");
-			} else
-				P_INFO("os: new record size = %i\n", new_size);
 
 			rc = appldata_diag(APPLDATA_RECORD_OS_ID,
 					   APPLDATA_STOP_REC,
@@ -204,9 +163,6 @@
 	}
 	os_data->timestamp = get_clock();
 	os_data->sync_count_2++;
-#ifdef APPLDATA_DEBUG
-	appldata_print_debug(os_data);
-#endif
 }
 
 
@@ -227,12 +183,9 @@
 		rc = -ENOMEM;
 		goto out;
 	}
-	P_DEBUG("max. sizeof(os) = %i, sizeof(os_cpu) = %lu\n", max_size,
-		sizeof(struct appldata_os_per_cpu));
 
 	appldata_os_data = kzalloc(max_size, GFP_DMA);
 	if (appldata_os_data == NULL) {
-		P_ERROR("No memory for %s!\n", ops.name);
 		rc = -ENOMEM;
 		goto out;
 	}
@@ -240,17 +193,12 @@
 	appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu);
 	appldata_os_data->cpu_offset   = offsetof(struct appldata_os_data,
 							os_cpu);
-	P_DEBUG("cpu offset = %u\n", appldata_os_data->cpu_offset);
 
 	ops.data = appldata_os_data;
 	ops.callback  = &appldata_get_os_data;
 	rc = appldata_register_ops(&ops);
-	if (rc != 0) {
-		P_ERROR("Error registering ops, rc = %i\n", rc);
+	if (rc != 0)
 		kfree(appldata_os_data);
-	} else {
-		P_DEBUG("%s-ops registered!\n", ops.name);
-	}
 out:
 	return rc;
 }
@@ -264,7 +212,6 @@
 {
 	appldata_unregister_ops(&ops);
 	kfree(appldata_os_data);
-	P_DEBUG("%s-ops unregistered!\n", ops.name);
 }
 
 
diff --git a/arch/s390/crypto/crypt_s390.h b/arch/s390/crypto/crypt_s390.h
index 9992f95..0ef9829 100644
--- a/arch/s390/crypto/crypt_s390.h
+++ b/arch/s390/crypto/crypt_s390.h
@@ -296,6 +296,10 @@
 	unsigned char status[16];
 	int ret;
 
+	/* check if CPACF facility (bit 17) is available */
+	if (!(stfl() & 1ULL << (31 - 17)))
+		return 0;
+
 	switch (func & CRYPT_S390_OP_MASK) {
 	case CRYPT_S390_KM:
 		ret = crypt_s390_km(KM_QUERY, &status, NULL, NULL, 0);
diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c
index 0cfefdd..6a4300b 100644
--- a/arch/s390/crypto/prng.c
+++ b/arch/s390/crypto/prng.c
@@ -185,11 +185,8 @@
 	prng_seed(16);
 
 	ret = misc_register(&prng_dev);
-	if (ret) {
-		printk(KERN_WARNING
-		       "Could not register misc device for PRNG.\n");
+	if (ret)
 		goto out_buf;
-	}
 	return 0;
 
 out_buf:
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 4b010ff..7383781 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -150,33 +150,24 @@
 			      unsigned long nr_segs, loff_t offset)
 {
 	char *data;
-	size_t len;
+	ssize_t ret;
 	struct file *filp = iocb->ki_filp;
 	/* XXX: temporary */
 	char __user *buf = iov[0].iov_base;
 	size_t count = iov[0].iov_len;
 
-	if (nr_segs != 1) {
-		count = -EINVAL;
-		goto out;
-	}
+	if (nr_segs != 1)
+		return -EINVAL;
 
 	data = filp->private_data;
-	len = strlen(data);
-	if (offset > len) {
-		count = 0;
-		goto out;
-	}
-	if (count > len - offset)
-		count = len - offset;
-	if (copy_to_user(buf, data + offset, count)) {
-		count = -EFAULT;
-		goto out;
-	}
-	iocb->ki_pos += count;
+	ret = simple_read_from_buffer(buf, count, &offset, data, strlen(data));
+	if (ret <= 0)
+		return ret;
+
+	iocb->ki_pos += ret;
 	file_accessed(filp);
-out:
-	return count;
+
+	return ret;
 }
 static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
 			      unsigned long nr_segs, loff_t offset)
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 6302f50..50f657e 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -7,9 +7,14 @@
 #
 CFLAGS_smp.o	:= -Wno-nonnull
 
+#
+# Pass UTS_MACHINE for user_regset definition
+#
+CFLAGS_ptrace.o		+= -DUTS_MACHINE='"$(UTS_MACHINE)"'
+
 obj-y	:=  bitmap.o traps.o time.o process.o base.o early.o \
             setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
-	    s390_ext.o debug.o irq.o ipl.o dis.o diag.o
+	    s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o
 
 obj-y	+= $(if $(CONFIG_64BIT),entry64.o,entry.o)
 obj-y	+= $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
@@ -23,7 +28,7 @@
 compat-obj-$(CONFIG_AUDIT)	+= compat_audit.o
 obj-$(CONFIG_COMPAT)		+= compat_linux.o compat_signal.o \
 					compat_wrapper.o compat_exec_domain.o \
-					binfmt_elf32.o $(compat-obj-y)
+					$(compat-obj-y)
 
 obj-$(CONFIG_VIRT_TIMER)	+= vtime.o
 obj-$(CONFIG_STACKTRACE)	+= stacktrace.o
diff --git a/arch/s390/kernel/binfmt_elf32.c b/arch/s390/kernel/binfmt_elf32.c
deleted file mode 100644
index 3e1c315..0000000
--- a/arch/s390/kernel/binfmt_elf32.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Support for 32-bit Linux for S390 ELF binaries.
- *
- * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Gerhard Tonn (ton@de.ibm.com)
- *
- * Heavily inspired by the 32-bit Sparc compat code which is
- * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com)
- * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek   (jj@ultra.linux.cz)
- */
-
-#define __ASMS390_ELF_H
-
-#include <linux/time.h>
-
-/*
- * These are used to set parameters in the core dumps.
- */
-#define ELF_CLASS	ELFCLASS32
-#define ELF_DATA	ELFDATA2MSB
-#define ELF_ARCH	EM_S390
-
-/*
- * This is used to ensure we don't load something for the wrong architecture.
- */
-#define elf_check_arch(x) \
-	(((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
-         && (x)->e_ident[EI_CLASS] == ELF_CLASS)
-
-/* ELF register definitions */
-#define NUM_GPRS      16
-#define NUM_FPRS      16
-#define NUM_ACRS      16    
-
-/* For SVR4/S390 the function pointer to be registered with `atexit` is
-   passed in R14. */
-#define ELF_PLAT_INIT(_r, load_addr) \
-	do { \
-		_r->gprs[14] = 0; \
-	} while(0)
-
-#define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE       4096
-
-/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
-   use of this is to invoke "./ld.so someprog" to test out a new version of
-   the loader.  We need to make sure that it is out of the way of the program
-   that it will "exec", and that there is sufficient room for the brk.  */
-
-#define ELF_ET_DYN_BASE         (TASK_SIZE / 3 * 2)
-
-/* Wow, the "main" arch needs arch dependent functions too.. :) */
-
-/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
-   now struct_user_regs, they are different) */
-
-#define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs32(regs, &pr_reg);
-
-#define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs32(tsk, regs)
-
-#define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs)
-
-/* This yields a mask that user programs can use to figure out what
-   instruction set this CPU supports. */
-
-#define ELF_HWCAP (0)
-
-/* This yields a string that ld.so will use to load implementation
-   specific libraries for optimization.  This is more specific in
-   intent than poking at uname or /proc/cpuinfo.
-
-   For the moment, we have only optimizations for the Intel generations,
-   but that could change... */
-
-#define ELF_PLATFORM (NULL)
-
-#define SET_PERSONALITY(ex, ibcs2)			\
-do {							\
-	if (ibcs2)                                      \
-		set_personality(PER_SVR4);              \
-	else if (current->personality != PER_LINUX32)   \
-		set_personality(PER_LINUX);             \
-	set_thread_flag(TIF_31BIT);			\
-} while (0)
-
-#include "compat_linux.h"
-
-typedef _s390_fp_regs32 elf_fpregset_t;
-
-typedef struct
-{
-	
-	_psw_t32	psw;
-	__u32		gprs[__NUM_GPRS]; 
-	__u32		acrs[__NUM_ACRS]; 
-	__u32		orig_gpr2;
-} s390_regs32;
-typedef s390_regs32 elf_gregset_t;
-
-static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs)
-{
-	int i;
-
-	memcpy(&regs->psw.mask, &ptregs->psw.mask, 4);
-	memcpy(&regs->psw.addr, (char *)&ptregs->psw.addr + 4, 4);
-	for (i = 0; i < NUM_GPRS; i++)
-		regs->gprs[i] = ptregs->gprs[i];
-	save_access_regs(regs->acrs);
-	regs->orig_gpr2 = ptregs->orig_gpr2;
-	return 1;
-}
-
-static inline int dump_task_regs32(struct task_struct *tsk, elf_gregset_t *regs)
-{
-	struct pt_regs *ptregs = task_pt_regs(tsk);
-	int i;
-
-	memcpy(&regs->psw.mask, &ptregs->psw.mask, 4);
-	memcpy(&regs->psw.addr, (char *)&ptregs->psw.addr + 4, 4);
-	for (i = 0; i < NUM_GPRS; i++)
-		regs->gprs[i] = ptregs->gprs[i];
-	memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs));
-	regs->orig_gpr2 = ptregs->orig_gpr2;
-	return 1;
-}
-
-static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
-{
-	if (tsk == current)
-		save_fp_regs((s390_fp_regs *) fpregs);
-	else
-		memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t));
-	return 1;
-}
-
-#include <asm/processor.h>
-#include <asm/pgalloc.h>
-#include <linux/module.h>
-#include <linux/elfcore.h>
-#include <linux/binfmts.h>
-#include <linux/compat.h>
-
-#define elf_prstatus elf_prstatus32
-struct elf_prstatus32
-{
-	struct elf_siginfo pr_info;	/* Info associated with signal */
-	short	pr_cursig;		/* Current signal */
-	u32	pr_sigpend;	/* Set of pending signals */
-	u32	pr_sighold;	/* Set of held signals */
-	pid_t	pr_pid;
-	pid_t	pr_ppid;
-	pid_t	pr_pgrp;
-	pid_t	pr_sid;
-	struct compat_timeval pr_utime;	/* User time */
-	struct compat_timeval pr_stime;	/* System time */
-	struct compat_timeval pr_cutime;	/* Cumulative user time */
-	struct compat_timeval pr_cstime;	/* Cumulative system time */
-	elf_gregset_t pr_reg;	/* GP registers */
-	int pr_fpvalid;		/* True if math co-processor being used.  */
-};
-
-#define elf_prpsinfo elf_prpsinfo32
-struct elf_prpsinfo32
-{
-	char	pr_state;	/* numeric process state */
-	char	pr_sname;	/* char for pr_state */
-	char	pr_zomb;	/* zombie */
-	char	pr_nice;	/* nice val */
-	u32	pr_flag;	/* flags */
-	u16	pr_uid;
-	u16	pr_gid;
-	pid_t	pr_pid, pr_ppid, pr_pgrp, pr_sid;
-	/* Lots missing */
-	char	pr_fname[16];	/* filename of executable */
-	char	pr_psargs[ELF_PRARGSZ];	/* initial part of arg list */
-};
-
-#include <linux/highuid.h>
-
-/*
-#define init_elf_binfmt init_elf32_binfmt
-*/
-
-#undef start_thread
-#define start_thread                    start_thread31 
-
-static inline void start_thread31(struct pt_regs *regs, unsigned long new_psw,
-				  unsigned long new_stackp)
-{
-	set_fs(USER_DS);
-	regs->psw.mask	= psw_user32_bits;
-	regs->psw.addr	= new_psw;
-	regs->gprs[15]	= new_stackp;
-	crst_table_downgrade(current->mm, 1UL << 31);
-}
-
-MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries,"
-                   " Copyright 2000 IBM Corporation"); 
-MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>");
-
-#undef MODULE_DESCRIPTION
-#undef MODULE_AUTHOR
-
-#undef cputime_to_timeval
-#define cputime_to_timeval cputime_to_compat_timeval
-static inline void
-cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
-{
-	value->tv_usec = cputime % 1000000;
-	value->tv_sec = cputime / 1000000;
-}
-
-#include "../../../fs/binfmt_elf.c"
-
diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h
index 419aef9..cde81fa 100644
--- a/arch/s390/kernel/compat_ptrace.h
+++ b/arch/s390/kernel/compat_ptrace.h
@@ -1,7 +1,7 @@
 #ifndef _PTRACE32_H
 #define _PTRACE32_H
 
-#include "compat_linux.h"  /* needed for _psw_t32 */
+#include "compat_linux.h"  /* needed for psw_compat_t */
 
 typedef struct {
 	__u32 cr[3];
@@ -38,7 +38,7 @@
 
 struct user_regs_struct32
 {
-	_psw_t32 psw;
+	psw_compat_t psw;
 	u32 gprs[NUM_GPRS];
 	u32 acrs[NUM_ACRS];
 	u32 orig_gpr2;
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
index c93d129..d80fcd4 100644
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -1079,7 +1079,6 @@
 	s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table);
 	mutex_lock(&debug_mutex);
 	debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT,NULL);
-	printk(KERN_INFO "debug: Initialization complete\n");
 	initialized = 1;
 	mutex_unlock(&debug_mutex);
 
@@ -1193,7 +1192,6 @@
 	for(; isspace(*buf); buf++);
 	rc = simple_strtoul(buf, &buf, 10);
 	if(*buf){
-		printk("debug: no integer specified!\n");
 		rc = -EINVAL;
 	}
 	return rc;
@@ -1340,19 +1338,12 @@
                         	memset(id->areas[i][j], 0, PAGE_SIZE);
 			}
 		}
-                printk(KERN_INFO "debug: %s: all areas flushed\n",id->name);
         } else if(area >= 0 && area < id->nr_areas) {
                 id->active_entries[area] = 0;
 		id->active_pages[area] = 0;
 		for(i = 0; i < id->pages_per_area; i++) {
                 	memset(id->areas[area][i],0,PAGE_SIZE);
 		}
-                printk(KERN_INFO "debug: %s: area %i has been flushed\n",
-                        id->name, area);
-        } else {
-                printk(KERN_INFO
-                      "debug: %s: area %i cannot be flushed (range: %i - %i)\n",
-                        id->name, area, 0, id->nr_areas-1);
         }
         spin_unlock_irqrestore(&id->lock,flags);
 }
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index d0e0968..2a2ca26 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/pfn.h>
 #include <linux/uaccess.h>
+#include <asm/ebcdic.h>
 #include <asm/ipl.h>
 #include <asm/lowcore.h>
 #include <asm/processor.h>
@@ -26,12 +27,40 @@
 /*
  * Create a Kernel NSS if the SAVESYS= parameter is defined
  */
-#define DEFSYS_CMD_SIZE		96
+#define DEFSYS_CMD_SIZE		128
 #define SAVESYS_CMD_SIZE	32
 
 char kernel_nss_name[NSS_NAME_SIZE + 1];
 
+static void __init setup_boot_command_line(void);
+
+
 #ifdef CONFIG_SHARED_KERNEL
+int __init savesys_ipl_nss(char *cmd, const int cmdlen);
+
+asm(
+	"	.section .init.text,\"ax\",@progbits\n"
+	"	.align	4\n"
+	"	.type	savesys_ipl_nss, @function\n"
+	"savesys_ipl_nss:\n"
+#ifdef CONFIG_64BIT
+	"	stmg	6,15,48(15)\n"
+	"	lgr	14,3\n"
+	"	sam31\n"
+	"	diag	2,14,0x8\n"
+	"	sam64\n"
+	"	lgr	2,14\n"
+	"	lmg	6,15,48(15)\n"
+#else
+	"	stm	6,15,24(15)\n"
+	"	lr	14,3\n"
+	"	diag	2,14,0x8\n"
+	"	lr	2,14\n"
+	"	lm	6,15,24(15)\n"
+#endif
+	"	br	14\n"
+	"	.size	savesys_ipl_nss, .-savesys_ipl_nss\n");
+
 static noinline __init void create_kernel_nss(void)
 {
 	unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size;
@@ -39,6 +68,7 @@
 	unsigned int sinitrd_pfn, einitrd_pfn;
 #endif
 	int response;
+	size_t len;
 	char *savesys_ptr;
 	char upper_command_line[COMMAND_LINE_SIZE];
 	char defsys_cmd[DEFSYS_CMD_SIZE];
@@ -49,8 +79,8 @@
 		return;
 
 	/* Convert COMMAND_LINE to upper case */
-	for (i = 0; i < strlen(COMMAND_LINE); i++)
-		upper_command_line[i] = toupper(COMMAND_LINE[i]);
+	for (i = 0; i < strlen(boot_command_line); i++)
+		upper_command_line[i] = toupper(boot_command_line[i]);
 
 	savesys_ptr = strstr(upper_command_line, "SAVESYS=");
 
@@ -83,7 +113,8 @@
 	}
 #endif
 
-	sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK", defsys_cmd, min_size);
+	sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK PARMREGS=0-13",
+		defsys_cmd, min_size);
 	sprintf(savesys_cmd, "SAVESYS %s \n IPL %s",
 		kernel_nss_name, kernel_nss_name);
 
@@ -94,13 +125,24 @@
 		return;
 	}
 
-	__cpcmd(savesys_cmd, NULL, 0, &response);
+	len = strlen(savesys_cmd);
+	ASCEBC(savesys_cmd, len);
+	response = savesys_ipl_nss(savesys_cmd, len);
 
-	if (response != strlen(savesys_cmd)) {
+	/* On success: response is equal to the command size,
+	 *	       max SAVESYS_CMD_SIZE
+	 * On error: response contains the numeric portion of cp error message.
+	 *	     for SAVESYS it will be >= 263
+	 */
+	if (response > SAVESYS_CMD_SIZE) {
 		kernel_nss_name[0] = '\0';
 		return;
 	}
 
+	/* re-setup boot command line with new ipl vm parms */
+	ipl_update_parameters();
+	setup_boot_command_line();
+
 	ipl_flags = IPL_NSS_VALID;
 }
 
@@ -141,109 +183,11 @@
 	if (cpuinfo->cpu_id.version == 0xff)
 		machine_flags |= MACHINE_FLAG_VM;
 
-	/* Running on a P/390 ? */
-	if (cpuinfo->cpu_id.machine == 0x7490)
-		machine_flags |= MACHINE_FLAG_P390;
-
 	/* Running under KVM ? */
 	if (cpuinfo->cpu_id.version == 0xfe)
 		machine_flags |= MACHINE_FLAG_KVM;
 }
 
-#ifdef CONFIG_64BIT
-static noinline __init int memory_fast_detect(void)
-{
-	unsigned long val0 = 0;
-	unsigned long val1 = 0xc;
-	int ret = -ENOSYS;
-
-	if (ipl_flags & IPL_NSS_VALID)
-		return -ENOSYS;
-
-	asm volatile(
-		"	diag	%1,%2,0x260\n"
-		"0:	lhi	%0,0\n"
-		"1:\n"
-		EX_TABLE(0b,1b)
-		: "+d" (ret), "+d" (val0), "+d" (val1) : : "cc");
-
-	if (ret || val0 != val1)
-		return -ENOSYS;
-
-	memory_chunk[0].size = val0 + 1;
-	return 0;
-}
-#else
-static inline int memory_fast_detect(void)
-{
-	return -ENOSYS;
-}
-#endif
-
-static inline __init unsigned long __tprot(unsigned long addr)
-{
-	int cc = -1;
-
-	asm volatile(
-		"	tprot	0(%1),0\n"
-		"0:	ipm	%0\n"
-		"	srl	%0,28\n"
-		"1:\n"
-		EX_TABLE(0b,1b)
-		: "+d" (cc) : "a" (addr) : "cc");
-	return (unsigned long)cc;
-}
-
-/* Checking memory in 128KB increments. */
-#define CHUNK_INCR	(1UL << 17)
-#define ADDR2G		(1UL << 31)
-
-static noinline __init void find_memory_chunks(unsigned long memsize)
-{
-	unsigned long addr = 0, old_addr = 0;
-	unsigned long old_cc = CHUNK_READ_WRITE;
-	unsigned long cc;
-	int chunk = 0;
-
-	while (chunk < MEMORY_CHUNKS) {
-		cc = __tprot(addr);
-		while (cc == old_cc) {
-			addr += CHUNK_INCR;
-			if (memsize && addr >= memsize)
-				break;
-#ifndef CONFIG_64BIT
-			if (addr == ADDR2G)
-				break;
-#endif
-			cc = __tprot(addr);
-		}
-
-		if (old_addr != addr &&
-		    (old_cc == CHUNK_READ_WRITE || old_cc == CHUNK_READ_ONLY)) {
-			memory_chunk[chunk].addr = old_addr;
-			memory_chunk[chunk].size = addr - old_addr;
-			memory_chunk[chunk].type = old_cc;
-			chunk++;
-		}
-
-		old_addr = addr;
-		old_cc = cc;
-
-#ifndef CONFIG_64BIT
-		if (addr == ADDR2G)
-			break;
-#endif
-		/*
-		 * Finish memory detection at the first hole
-		 * if storage size is unknown.
-		 */
-		if (cc == -1UL && !memsize)
-			break;
-		if (memsize && addr >= memsize)
-			break;
-	}
-}
-
 static __init void early_pgm_check_handler(void)
 {
 	unsigned long addr;
@@ -380,23 +324,61 @@
 #endif
 }
 
+static __init void rescue_initrd(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+	/*
+	 * Move the initrd right behind the bss section in case it starts
+	 * within the bss section. So we don't overwrite it when the bss
+	 * section gets cleared.
+	 */
+	if (!INITRD_START || !INITRD_SIZE)
+		return;
+	if (INITRD_START >= (unsigned long) __bss_stop)
+		return;
+	memmove(__bss_stop, (void *) INITRD_START, INITRD_SIZE);
+	INITRD_START = (unsigned long) __bss_stop;
+#endif
+}
+
+/* Set up boot command line */
+static void __init setup_boot_command_line(void)
+{
+	char *parm = NULL;
+
+	/* copy arch command line */
+	strlcpy(boot_command_line, COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
+	boot_command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0;
+
+	/* append IPL PARM data to the boot command line */
+	if (MACHINE_IS_VM) {
+		parm = boot_command_line + strlen(boot_command_line);
+		*parm++ = ' ';
+		get_ipl_vmparm(parm);
+		if (parm[0] == '=')
+			memmove(boot_command_line, parm + 1, strlen(parm));
+	}
+}
+
+
 /*
  * Save ipl parameters, clear bss memory, initialize storage keys
  * and create a kernel NSS at startup if the SAVESYS= parm is defined
  */
 void __init startup_init(void)
 {
-	unsigned long long memsize;
-
 	ipl_save_parameters();
+	rescue_initrd();
 	clear_bss_section();
 	init_kernel_storage_key();
 	lockdep_init();
 	lockdep_off();
-	detect_machine_type();
-	create_kernel_nss();
 	sort_main_extable();
 	setup_lowcore_early();
+	detect_machine_type();
+	ipl_update_parameters();
+	setup_boot_command_line();
+	create_kernel_nss();
 	detect_mvpg();
 	detect_ieee();
 	detect_csp();
@@ -404,18 +386,7 @@
 	detect_diag44();
 	detect_machine_facilities();
 	setup_hpage();
-	sclp_read_info_early();
 	sclp_facilities_detect();
-	memsize = sclp_memory_detect();
-#ifndef CONFIG_64BIT
-	/*
-	 * Can't deal with more than 2G in 31 bit addressing mode, so
-	 * limit the value in order to avoid strange side effects.
-	 */
-	if (memsize > ADDR2G)
-		memsize = ADDR2G;
-#endif
-	if (memory_fast_detect() < 0)
-		find_memory_chunks((unsigned long) memsize);
+	detect_memory_layout(memory_chunk);
 	lockdep_on();
 }
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index 5325424..54b2779 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -14,6 +14,7 @@
 #include <linux/delay.h>
 #include <linux/reboot.h>
 #include <linux/ctype.h>
+#include <linux/fs.h>
 #include <asm/ipl.h>
 #include <asm/smp.h>
 #include <asm/setup.h>
@@ -22,6 +23,7 @@
 #include <asm/ebcdic.h>
 #include <asm/reset.h>
 #include <asm/sclp.h>
+#include <asm/setup.h>
 
 #define IPL_PARM_BLOCK_VERSION 0
 
@@ -121,6 +123,7 @@
 	REIPL_METHOD_FCP_RO_VM,
 	REIPL_METHOD_FCP_DUMP,
 	REIPL_METHOD_NSS,
+	REIPL_METHOD_NSS_DIAG,
 	REIPL_METHOD_DEFAULT,
 };
 
@@ -134,14 +137,15 @@
 
 static int diag308_set_works = 0;
 
+static struct ipl_parameter_block ipl_block;
+
 static int reipl_capabilities = IPL_TYPE_UNKNOWN;
 
 static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
 static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT;
 static struct ipl_parameter_block *reipl_block_fcp;
 static struct ipl_parameter_block *reipl_block_ccw;
-
-static char reipl_nss_name[NSS_NAME_SIZE + 1];
+static struct ipl_parameter_block *reipl_block_nss;
 
 static int dump_capabilities = DUMP_TYPE_NONE;
 static enum dump_type dump_type = DUMP_TYPE_NONE;
@@ -263,6 +267,56 @@
 
 static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
 
+/* VM IPL PARM routines */
+static void reipl_get_ascii_vmparm(char *dest,
+				   const struct ipl_parameter_block *ipb)
+{
+	int i;
+	int len = 0;
+	char has_lowercase = 0;
+
+	if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) &&
+	    (ipb->ipl_info.ccw.vm_parm_len > 0)) {
+
+		len = ipb->ipl_info.ccw.vm_parm_len;
+		memcpy(dest, ipb->ipl_info.ccw.vm_parm, len);
+		/* If at least one character is lowercase, we assume mixed
+		 * case; otherwise we convert everything to lowercase.
+		 */
+		for (i = 0; i < len; i++)
+			if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */
+			    (dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */
+			    (dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */
+				has_lowercase = 1;
+				break;
+			}
+		if (!has_lowercase)
+			EBC_TOLOWER(dest, len);
+		EBCASC(dest, len);
+	}
+	dest[len] = 0;
+}
+
+void get_ipl_vmparm(char *dest)
+{
+	if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW))
+		reipl_get_ascii_vmparm(dest, &ipl_block);
+	else
+		dest[0] = 0;
+}
+
+static ssize_t ipl_vm_parm_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *page)
+{
+	char parm[DIAG308_VMPARM_SIZE + 1] = {};
+
+	get_ipl_vmparm(parm);
+	return sprintf(page, "%s\n", parm);
+}
+
+static struct kobj_attribute sys_ipl_vm_parm_attr =
+	__ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL);
+
 static ssize_t sys_ipl_device_show(struct kobject *kobj,
 				   struct kobj_attribute *attr, char *page)
 {
@@ -285,14 +339,8 @@
 static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr,
 				  char *buf, loff_t off, size_t count)
 {
-	unsigned int size = IPL_PARMBLOCK_SIZE;
-
-	if (off > size)
-		return 0;
-	if (off + count > size)
-		count = size - off;
-	memcpy(buf, (void *)IPL_PARMBLOCK_START + off, count);
-	return count;
+	return memory_read_from_buffer(buf, count, &off, IPL_PARMBLOCK_START,
+					IPL_PARMBLOCK_SIZE);
 }
 
 static struct bin_attribute ipl_parameter_attr = {
@@ -310,12 +358,7 @@
 	unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len;
 	void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data;
 
-	if (off > size)
-		return 0;
-	if (off + count > size)
-		count = size - off;
-	memcpy(buf, scp_data + off, count);
-	return count;
+	return memory_read_from_buffer(buf, count, &off, scp_data, size);
 }
 
 static struct bin_attribute ipl_scp_data_attr = {
@@ -370,15 +413,27 @@
 static struct kobj_attribute sys_ipl_ccw_loadparm_attr =
 	__ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL);
 
-static struct attribute *ipl_ccw_attrs[] = {
+static struct attribute *ipl_ccw_attrs_vm[] = {
+	&sys_ipl_type_attr.attr,
+	&sys_ipl_device_attr.attr,
+	&sys_ipl_ccw_loadparm_attr.attr,
+	&sys_ipl_vm_parm_attr.attr,
+	NULL,
+};
+
+static struct attribute *ipl_ccw_attrs_lpar[] = {
 	&sys_ipl_type_attr.attr,
 	&sys_ipl_device_attr.attr,
 	&sys_ipl_ccw_loadparm_attr.attr,
 	NULL,
 };
 
-static struct attribute_group ipl_ccw_attr_group = {
-	.attrs = ipl_ccw_attrs,
+static struct attribute_group ipl_ccw_attr_group_vm = {
+	.attrs = ipl_ccw_attrs_vm,
+};
+
+static struct attribute_group ipl_ccw_attr_group_lpar = {
+	.attrs = ipl_ccw_attrs_lpar
 };
 
 /* NSS ipl device attributes */
@@ -388,6 +443,8 @@
 static struct attribute *ipl_nss_attrs[] = {
 	&sys_ipl_type_attr.attr,
 	&sys_ipl_nss_name_attr.attr,
+	&sys_ipl_ccw_loadparm_attr.attr,
+	&sys_ipl_vm_parm_attr.attr,
 	NULL,
 };
 
@@ -450,7 +507,12 @@
 	}
 	switch (ipl_info.type) {
 	case IPL_TYPE_CCW:
-		rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group);
+		if (MACHINE_IS_VM)
+			rc = sysfs_create_group(&ipl_kset->kobj,
+						&ipl_ccw_attr_group_vm);
+		else
+			rc = sysfs_create_group(&ipl_kset->kobj,
+						&ipl_ccw_attr_group_lpar);
 		break;
 	case IPL_TYPE_FCP:
 	case IPL_TYPE_FCP_DUMP:
@@ -481,6 +543,83 @@
  * reipl shutdown action: Reboot Linux on shutdown.
  */
 
+/* VM IPL PARM attributes */
+static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb,
+					  char *page)
+{
+	char vmparm[DIAG308_VMPARM_SIZE + 1] = {};
+
+	reipl_get_ascii_vmparm(vmparm, ipb);
+	return sprintf(page, "%s\n", vmparm);
+}
+
+static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb,
+					  size_t vmparm_max,
+					  const char *buf, size_t len)
+{
+	int i, ip_len;
+
+	/* ignore trailing newline */
+	ip_len = len;
+	if ((len > 0) && (buf[len - 1] == '\n'))
+		ip_len--;
+
+	if (ip_len > vmparm_max)
+		return -EINVAL;
+
+	/* parm is used to store kernel options, check for common chars */
+	for (i = 0; i < ip_len; i++)
+		if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i])))
+			return -EINVAL;
+
+	memset(ipb->ipl_info.ccw.vm_parm, 0, DIAG308_VMPARM_SIZE);
+	ipb->ipl_info.ccw.vm_parm_len = ip_len;
+	if (ip_len > 0) {
+		ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
+		memcpy(ipb->ipl_info.ccw.vm_parm, buf, ip_len);
+		ASCEBC(ipb->ipl_info.ccw.vm_parm, ip_len);
+	} else {
+		ipb->ipl_info.ccw.vm_flags &= ~DIAG308_VM_FLAGS_VP_VALID;
+	}
+
+	return len;
+}
+
+/* NSS wrapper */
+static ssize_t reipl_nss_vmparm_show(struct kobject *kobj,
+				     struct kobj_attribute *attr, char *page)
+{
+	return reipl_generic_vmparm_show(reipl_block_nss, page);
+}
+
+static ssize_t reipl_nss_vmparm_store(struct kobject *kobj,
+				      struct kobj_attribute *attr,
+				      const char *buf, size_t len)
+{
+	return reipl_generic_vmparm_store(reipl_block_nss, 56, buf, len);
+}
+
+/* CCW wrapper */
+static ssize_t reipl_ccw_vmparm_show(struct kobject *kobj,
+				     struct kobj_attribute *attr, char *page)
+{
+	return reipl_generic_vmparm_show(reipl_block_ccw, page);
+}
+
+static ssize_t reipl_ccw_vmparm_store(struct kobject *kobj,
+				      struct kobj_attribute *attr,
+				      const char *buf, size_t len)
+{
+	return reipl_generic_vmparm_store(reipl_block_ccw, 64, buf, len);
+}
+
+static struct kobj_attribute sys_reipl_nss_vmparm_attr =
+	__ATTR(parm, S_IRUGO | S_IWUSR, reipl_nss_vmparm_show,
+					reipl_nss_vmparm_store);
+static struct kobj_attribute sys_reipl_ccw_vmparm_attr =
+	__ATTR(parm, S_IRUGO | S_IWUSR, reipl_ccw_vmparm_show,
+					reipl_ccw_vmparm_store);
+
 /* FCP reipl device attributes */
 
 DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n",
@@ -513,27 +652,26 @@
 DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
 	reipl_block_ccw->ipl_info.ccw.devno);
 
-static void reipl_get_ascii_loadparm(char *loadparm)
+static void reipl_get_ascii_loadparm(char *loadparm,
+				     struct ipl_parameter_block *ibp)
 {
-	memcpy(loadparm, &reipl_block_ccw->ipl_info.ccw.load_param,
-	       LOADPARM_LEN);
+	memcpy(loadparm, ibp->ipl_info.ccw.load_parm, LOADPARM_LEN);
 	EBCASC(loadparm, LOADPARM_LEN);
 	loadparm[LOADPARM_LEN] = 0;
 	strstrip(loadparm);
 }
 
-static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj,
-				       struct kobj_attribute *attr, char *page)
+static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb,
+					   char *page)
 {
 	char buf[LOADPARM_LEN + 1];
 
-	reipl_get_ascii_loadparm(buf);
+	reipl_get_ascii_loadparm(buf, ipb);
 	return sprintf(page, "%s\n", buf);
 }
 
-static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj,
-					struct kobj_attribute *attr,
-					const char *buf, size_t len)
+static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb,
+					    const char *buf, size_t len)
 {
 	int i, lp_len;
 
@@ -552,35 +690,128 @@
 		return -EINVAL;
 	}
 	/* initialize loadparm with blanks */
-	memset(&reipl_block_ccw->ipl_info.ccw.load_param, ' ', LOADPARM_LEN);
+	memset(ipb->ipl_info.ccw.load_parm, ' ', LOADPARM_LEN);
 	/* copy and convert to ebcdic */
-	memcpy(&reipl_block_ccw->ipl_info.ccw.load_param, buf, lp_len);
-	ASCEBC(reipl_block_ccw->ipl_info.ccw.load_param, LOADPARM_LEN);
+	memcpy(ipb->ipl_info.ccw.load_parm, buf, lp_len);
+	ASCEBC(ipb->ipl_info.ccw.load_parm, LOADPARM_LEN);
 	return len;
 }
 
-static struct kobj_attribute sys_reipl_ccw_loadparm_attr =
-	__ATTR(loadparm, 0644, reipl_ccw_loadparm_show,
-	       reipl_ccw_loadparm_store);
+/* NSS wrapper */
+static ssize_t reipl_nss_loadparm_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *page)
+{
+	return reipl_generic_loadparm_show(reipl_block_nss, page);
+}
 
-static struct attribute *reipl_ccw_attrs[] = {
+static ssize_t reipl_nss_loadparm_store(struct kobject *kobj,
+					struct kobj_attribute *attr,
+					const char *buf, size_t len)
+{
+	return reipl_generic_loadparm_store(reipl_block_nss, buf, len);
+}
+
+/* CCW wrapper */
+static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj,
+				       struct kobj_attribute *attr, char *page)
+{
+	return reipl_generic_loadparm_show(reipl_block_ccw, page);
+}
+
+static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj,
+					struct kobj_attribute *attr,
+					const char *buf, size_t len)
+{
+	return reipl_generic_loadparm_store(reipl_block_ccw, buf, len);
+}
+
+static struct kobj_attribute sys_reipl_ccw_loadparm_attr =
+	__ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_ccw_loadparm_show,
+					    reipl_ccw_loadparm_store);
+
+static struct attribute *reipl_ccw_attrs_vm[] = {
+	&sys_reipl_ccw_device_attr.attr,
+	&sys_reipl_ccw_loadparm_attr.attr,
+	&sys_reipl_ccw_vmparm_attr.attr,
+	NULL,
+};
+
+static struct attribute *reipl_ccw_attrs_lpar[] = {
 	&sys_reipl_ccw_device_attr.attr,
 	&sys_reipl_ccw_loadparm_attr.attr,
 	NULL,
 };
 
-static struct attribute_group reipl_ccw_attr_group = {
+static struct attribute_group reipl_ccw_attr_group_vm = {
 	.name  = IPL_CCW_STR,
-	.attrs = reipl_ccw_attrs,
+	.attrs = reipl_ccw_attrs_vm,
+};
+
+static struct attribute_group reipl_ccw_attr_group_lpar = {
+	.name  = IPL_CCW_STR,
+	.attrs = reipl_ccw_attrs_lpar,
 };
 
 
 /* NSS reipl device attributes */
+static void reipl_get_ascii_nss_name(char *dst,
+				     struct ipl_parameter_block *ipb)
+{
+	memcpy(dst, ipb->ipl_info.ccw.nss_name, NSS_NAME_SIZE);
+	EBCASC(dst, NSS_NAME_SIZE);
+	dst[NSS_NAME_SIZE] = 0;
+}
 
-DEFINE_IPL_ATTR_STR_RW(reipl_nss, name, "%s\n", "%s\n", reipl_nss_name);
+static ssize_t reipl_nss_name_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *page)
+{
+	char nss_name[NSS_NAME_SIZE + 1] = {};
+
+	reipl_get_ascii_nss_name(nss_name, reipl_block_nss);
+	return sprintf(page, "%s\n", nss_name);
+}
+
+static ssize_t reipl_nss_name_store(struct kobject *kobj,
+				    struct kobj_attribute *attr,
+				    const char *buf, size_t len)
+{
+	int nss_len;
+
+	/* ignore trailing newline */
+	nss_len = len;
+	if ((len > 0) && (buf[len - 1] == '\n'))
+		nss_len--;
+
+	if (nss_len > NSS_NAME_SIZE)
+		return -EINVAL;
+
+	memset(reipl_block_nss->ipl_info.ccw.nss_name, 0x40, NSS_NAME_SIZE);
+	if (nss_len > 0) {
+		reipl_block_nss->ipl_info.ccw.vm_flags |=
+			DIAG308_VM_FLAGS_NSS_VALID;
+		memcpy(reipl_block_nss->ipl_info.ccw.nss_name, buf, nss_len);
+		ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
+		EBC_TOUPPER(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
+	} else {
+		reipl_block_nss->ipl_info.ccw.vm_flags &=
+			~DIAG308_VM_FLAGS_NSS_VALID;
+	}
+
+	return len;
+}
+
+static struct kobj_attribute sys_reipl_nss_name_attr =
+	__ATTR(name, S_IRUGO | S_IWUSR, reipl_nss_name_show,
+					reipl_nss_name_store);
+
+static struct kobj_attribute sys_reipl_nss_loadparm_attr =
+	__ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_nss_loadparm_show,
+					    reipl_nss_loadparm_store);
 
 static struct attribute *reipl_nss_attrs[] = {
 	&sys_reipl_nss_name_attr.attr,
+	&sys_reipl_nss_loadparm_attr.attr,
+	&sys_reipl_nss_vmparm_attr.attr,
 	NULL,
 };
 
@@ -617,7 +848,10 @@
 		reipl_method = REIPL_METHOD_FCP_DUMP;
 		break;
 	case IPL_TYPE_NSS:
-		reipl_method = REIPL_METHOD_NSS;
+		if (diag308_set_works)
+			reipl_method = REIPL_METHOD_NSS_DIAG;
+		else
+			reipl_method = REIPL_METHOD_NSS;
 		break;
 	case IPL_TYPE_UNKNOWN:
 		reipl_method = REIPL_METHOD_DEFAULT;
@@ -655,11 +889,38 @@
 
 static struct kset *reipl_kset;
 
+static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb,
+			   const enum ipl_method m)
+{
+	char loadparm[LOADPARM_LEN + 1] = {};
+	char vmparm[DIAG308_VMPARM_SIZE + 1] = {};
+	char nss_name[NSS_NAME_SIZE + 1] = {};
+	size_t pos = 0;
+
+	reipl_get_ascii_loadparm(loadparm, ipb);
+	reipl_get_ascii_nss_name(nss_name, ipb);
+	reipl_get_ascii_vmparm(vmparm, ipb);
+
+	switch (m) {
+	case REIPL_METHOD_CCW_VM:
+		pos = sprintf(dst, "IPL %X CLEAR", ipb->ipl_info.ccw.devno);
+		break;
+	case REIPL_METHOD_NSS:
+		pos = sprintf(dst, "IPL %s", nss_name);
+		break;
+	default:
+		break;
+	}
+	if (strlen(loadparm) > 0)
+		pos += sprintf(dst + pos, " LOADPARM '%s'", loadparm);
+	if (strlen(vmparm) > 0)
+		sprintf(dst + pos, " PARM %s", vmparm);
+}
+
 static void reipl_run(struct shutdown_trigger *trigger)
 {
 	struct ccw_dev_id devid;
-	static char buf[100];
-	char loadparm[LOADPARM_LEN + 1];
+	static char buf[128];
 
 	switch (reipl_method) {
 	case REIPL_METHOD_CCW_CIO:
@@ -668,13 +929,7 @@
 		reipl_ccw_dev(&devid);
 		break;
 	case REIPL_METHOD_CCW_VM:
-		reipl_get_ascii_loadparm(loadparm);
-		if (strlen(loadparm) == 0)
-			sprintf(buf, "IPL %X CLEAR",
-				reipl_block_ccw->ipl_info.ccw.devno);
-		else
-			sprintf(buf, "IPL %X CLEAR LOADPARM '%s'",
-				reipl_block_ccw->ipl_info.ccw.devno, loadparm);
+		get_ipl_string(buf, reipl_block_ccw, REIPL_METHOD_CCW_VM);
 		__cpcmd(buf, NULL, 0, NULL);
 		break;
 	case REIPL_METHOD_CCW_DIAG:
@@ -691,8 +946,12 @@
 	case REIPL_METHOD_FCP_RO_VM:
 		__cpcmd("IPL", NULL, 0, NULL);
 		break;
+	case REIPL_METHOD_NSS_DIAG:
+		diag308(DIAG308_SET, reipl_block_nss);
+		diag308(DIAG308_IPL, NULL);
+		break;
 	case REIPL_METHOD_NSS:
-		sprintf(buf, "IPL %s", reipl_nss_name);
+		get_ipl_string(buf, reipl_block_nss, REIPL_METHOD_NSS);
 		__cpcmd(buf, NULL, 0, NULL);
 		break;
 	case REIPL_METHOD_DEFAULT:
@@ -707,16 +966,36 @@
 	disabled_wait((unsigned long) __builtin_return_address(0));
 }
 
-static void __init reipl_probe(void)
+static void reipl_block_ccw_init(struct ipl_parameter_block *ipb)
 {
-	void *buffer;
+	ipb->hdr.len = IPL_PARM_BLK_CCW_LEN;
+	ipb->hdr.version = IPL_PARM_BLOCK_VERSION;
+	ipb->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
+	ipb->hdr.pbt = DIAG308_IPL_TYPE_CCW;
+}
 
-	buffer = (void *) get_zeroed_page(GFP_KERNEL);
-	if (!buffer)
-		return;
-	if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK)
-		diag308_set_works = 1;
-	free_page((unsigned long)buffer);
+static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb)
+{
+	/* LOADPARM */
+	/* check if read scp info worked and set loadparm */
+	if (sclp_ipl_info.is_valid)
+		memcpy(ipb->ipl_info.ccw.load_parm,
+				&sclp_ipl_info.loadparm, LOADPARM_LEN);
+	else
+		/* read scp info failed: set empty loadparm (EBCDIC blanks) */
+		memset(ipb->ipl_info.ccw.load_parm, 0x40, LOADPARM_LEN);
+	ipb->hdr.flags = DIAG308_FLAGS_LP_VALID;
+
+	/* VM PARM */
+	if (MACHINE_IS_VM && diag308_set_works &&
+	    (ipl_block.ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID)) {
+
+		ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
+		ipb->ipl_info.ccw.vm_parm_len =
+					ipl_block.ipl_info.ccw.vm_parm_len;
+		memcpy(ipb->ipl_info.ccw.vm_parm,
+		       ipl_block.ipl_info.ccw.vm_parm, DIAG308_VMPARM_SIZE);
+	}
 }
 
 static int __init reipl_nss_init(void)
@@ -725,10 +1004,31 @@
 
 	if (!MACHINE_IS_VM)
 		return 0;
+
+	reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL);
+	if (!reipl_block_nss)
+		return -ENOMEM;
+
+	if (!diag308_set_works)
+		sys_reipl_nss_vmparm_attr.attr.mode = S_IRUGO;
+
 	rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group);
 	if (rc)
 		return rc;
-	strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1);
+
+	reipl_block_ccw_init(reipl_block_nss);
+	if (ipl_info.type == IPL_TYPE_NSS) {
+		memset(reipl_block_nss->ipl_info.ccw.nss_name,
+			' ', NSS_NAME_SIZE);
+		memcpy(reipl_block_nss->ipl_info.ccw.nss_name,
+			kernel_nss_name, strlen(kernel_nss_name));
+		ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, NSS_NAME_SIZE);
+		reipl_block_nss->ipl_info.ccw.vm_flags |=
+			DIAG308_VM_FLAGS_NSS_VALID;
+
+		reipl_block_ccw_fill_parms(reipl_block_nss);
+	}
+
 	reipl_capabilities |= IPL_TYPE_NSS;
 	return 0;
 }
@@ -740,28 +1040,27 @@
 	reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
 	if (!reipl_block_ccw)
 		return -ENOMEM;
-	rc = sysfs_create_group(&reipl_kset->kobj, &reipl_ccw_attr_group);
-	if (rc) {
-		free_page((unsigned long)reipl_block_ccw);
-		return rc;
+
+	if (MACHINE_IS_VM) {
+		if (!diag308_set_works)
+			sys_reipl_ccw_vmparm_attr.attr.mode = S_IRUGO;
+		rc = sysfs_create_group(&reipl_kset->kobj,
+					&reipl_ccw_attr_group_vm);
+	} else {
+		if(!diag308_set_works)
+			sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
+		rc = sysfs_create_group(&reipl_kset->kobj,
+					&reipl_ccw_attr_group_lpar);
 	}
-	reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
-	reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
-	reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
-	reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
-	reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID;
-	/* check if read scp info worked and set loadparm */
-	if (sclp_ipl_info.is_valid)
-		memcpy(reipl_block_ccw->ipl_info.ccw.load_param,
-		       &sclp_ipl_info.loadparm, LOADPARM_LEN);
-	else
-		/* read scp info failed: set empty loadparm (EBCDIC blanks) */
-		memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40,
-		       LOADPARM_LEN);
-	if (!MACHINE_IS_VM && !diag308_set_works)
-		sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
-	if (ipl_info.type == IPL_TYPE_CCW)
+	if (rc)
+		return rc;
+
+	reipl_block_ccw_init(reipl_block_ccw);
+	if (ipl_info.type == IPL_TYPE_CCW) {
 		reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
+		reipl_block_ccw_fill_parms(reipl_block_ccw);
+	}
+
 	reipl_capabilities |= IPL_TYPE_CCW;
 	return 0;
 }
@@ -1298,7 +1597,6 @@
 
 static int __init s390_ipl_init(void)
 {
-	reipl_probe();
 	sclp_get_ipl_info(&sclp_ipl_info);
 	shutdown_actions_init();
 	shutdown_triggers_init();
@@ -1405,6 +1703,12 @@
 	atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
 }
 
+void __init ipl_update_parameters(void)
+{
+	if (diag308(DIAG308_STORE, &ipl_block) == DIAG308_RC_OK)
+		diag308_set_works = 1;
+}
+
 void __init ipl_save_parameters(void)
 {
 	struct cio_iplinfo iplinfo;
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index ed04d13..288ad49 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -41,10 +41,8 @@
 	if (is_prohibited_opcode((kprobe_opcode_t *) p->addr))
 		return -EINVAL;
 
-	if ((unsigned long)p->addr & 0x01) {
-		printk("Attempt to register kprobe at an unaligned address\n");
+	if ((unsigned long)p->addr & 0x01)
 		return -EINVAL;
-		}
 
 	/* Use the get_insn_slot() facility for correctness */
 	if (!(p->ainsn.insn = get_insn_slot()))
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c
index 3c77dd3..131d7ee 100644
--- a/arch/s390/kernel/machine_kexec.c
+++ b/arch/s390/kernel/machine_kexec.c
@@ -52,7 +52,6 @@
 
 void machine_shutdown(void)
 {
-	printk(KERN_INFO "kexec: machine_shutdown called\n");
 }
 
 void machine_kexec(struct kimage *image)
diff --git a/arch/s390/kernel/mem_detect.c b/arch/s390/kernel/mem_detect.c
new file mode 100644
index 0000000..18ed7ab
--- /dev/null
+++ b/arch/s390/kernel/mem_detect.c
@@ -0,0 +1,100 @@
+/*
+ *    Copyright IBM Corp. 2008
+ *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/ipl.h>
+#include <asm/sclp.h>
+#include <asm/setup.h>
+
+static int memory_fast_detect(struct mem_chunk *chunk)
+{
+	unsigned long val0 = 0;
+	unsigned long val1 = 0xc;
+	int rc = -EOPNOTSUPP;
+
+	if (ipl_flags & IPL_NSS_VALID)
+		return -EOPNOTSUPP;
+	asm volatile(
+		"	diag	%1,%2,0x260\n"
+		"0:	lhi	%0,0\n"
+		"1:\n"
+		EX_TABLE(0b,1b)
+		: "+d" (rc), "+d" (val0), "+d" (val1) : : "cc");
+
+	if (rc || val0 != val1)
+		return -EOPNOTSUPP;
+	chunk->size = val0 + 1;
+	return 0;
+}
+
+static inline int tprot(unsigned long addr)
+{
+	int rc = -EFAULT;
+
+	asm volatile(
+		"	tprot	0(%1),0\n"
+		"0:	ipm	%0\n"
+		"	srl	%0,28\n"
+		"1:\n"
+		EX_TABLE(0b,1b)
+		: "+d" (rc) : "a" (addr) : "cc");
+	return rc;
+}
+
+#define ADDR2G (1ULL << 31)
+
+static void find_memory_chunks(struct mem_chunk chunk[])
+{
+	unsigned long long memsize, rnmax, rzm;
+	unsigned long addr = 0, size;
+	int i = 0, type;
+
+	rzm = sclp_get_rzm();
+	rnmax = sclp_get_rnmax();
+	memsize = rzm * rnmax;
+	if (!rzm)
+		rzm = 1ULL << 17;
+	if (sizeof(long) == 4) {
+		rzm = min(ADDR2G, rzm);
+		memsize = memsize ? min(ADDR2G, memsize) : ADDR2G;
+	}
+	do {
+		size = 0;
+		type = tprot(addr);
+		do {
+			size += rzm;
+			if (memsize && addr + size >= memsize)
+				break;
+		} while (type == tprot(addr + size));
+		if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) {
+			chunk[i].addr = addr;
+			chunk[i].size = size;
+			chunk[i].type = type;
+			i++;
+		}
+		addr += size;
+	} while (addr < memsize && i < MEMORY_CHUNKS);
+}
+
+void detect_memory_layout(struct mem_chunk chunk[])
+{
+	unsigned long flags, cr0;
+
+	memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk));
+	if (memory_fast_detect(&chunk[0]) == 0)
+		return;
+	/* Disable IRQs, DAT and low address protection so tprot does the
+	 * right thing and we don't get scheduled away with low address
+	 * protection disabled.
+	 */
+	flags = __raw_local_irq_stnsm(0xf8);
+	__ctl_store(cr0, 0, 0);
+	__ctl_clear_bit(0, 28);
+	find_memory_chunks(chunk);
+	__ctl_load(cr0, 0, 0);
+	__raw_local_irq_ssm(flags);
+}
+EXPORT_SYMBOL(detect_memory_layout);
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index 7920861..85defd0 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -75,46 +75,19 @@
 	return sf->gprs[8];
 }
 
-/*
- * Need to know about CPUs going idle?
- */
-static ATOMIC_NOTIFIER_HEAD(idle_chain);
 DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
 
-int register_idle_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_register(&idle_chain, nb);
-}
-EXPORT_SYMBOL(register_idle_notifier);
-
-int unregister_idle_notifier(struct notifier_block *nb)
-{
-	return atomic_notifier_chain_unregister(&idle_chain, nb);
-}
-EXPORT_SYMBOL(unregister_idle_notifier);
-
 static int s390_idle_enter(void)
 {
 	struct s390_idle_data *idle;
-	int nr_calls = 0;
-	void *hcpu;
-	int rc;
 
-	hcpu = (void *)(long)smp_processor_id();
-	rc = __atomic_notifier_call_chain(&idle_chain, S390_CPU_IDLE, hcpu, -1,
-					  &nr_calls);
-	if (rc == NOTIFY_BAD) {
-		nr_calls--;
-		__atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE,
-					     hcpu, nr_calls, NULL);
-		return rc;
-	}
 	idle = &__get_cpu_var(s390_idle);
 	spin_lock(&idle->lock);
 	idle->idle_count++;
 	idle->in_idle = 1;
 	idle->idle_enter = get_clock();
 	spin_unlock(&idle->lock);
+	vtime_stop_cpu_timer();
 	return NOTIFY_OK;
 }
 
@@ -122,13 +95,12 @@
 {
 	struct s390_idle_data *idle;
 
+	vtime_start_cpu_timer();
 	idle = &__get_cpu_var(s390_idle);
 	spin_lock(&idle->lock);
 	idle->idle_time += get_clock() - idle->idle_enter;
 	idle->in_idle = 0;
 	spin_unlock(&idle->lock);
-	atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE,
-				   (void *)(long) smp_processor_id());
 }
 
 extern void s390_handle_mcck(void);
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 35827b9..2815bfe 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -33,6 +33,8 @@
 #include <linux/security.h>
 #include <linux/audit.h>
 #include <linux/signal.h>
+#include <linux/elf.h>
+#include <linux/regset.h>
 
 #include <asm/segment.h>
 #include <asm/page.h>
@@ -47,6 +49,11 @@
 #include "compat_ptrace.h"
 #endif
 
+enum s390_regset {
+	REGSET_GENERAL,
+	REGSET_FP,
+};
+
 static void
 FixPerRegisters(struct task_struct *task)
 {
@@ -126,24 +133,10 @@
  * struct user contain pad bytes that should be read as zeroes.
  * Lovely...
  */
-static int
-peek_user(struct task_struct *child, addr_t addr, addr_t data)
+static unsigned long __peek_user(struct task_struct *child, addr_t addr)
 {
 	struct user *dummy = NULL;
-	addr_t offset, tmp, mask;
-
-	/*
-	 * Stupid gdb peeks/pokes the access registers in 64 bit with
-	 * an alignment of 4. Programmers from hell...
-	 */
-	mask = __ADDR_MASK;
-#ifdef CONFIG_64BIT
-	if (addr >= (addr_t) &dummy->regs.acrs &&
-	    addr < (addr_t) &dummy->regs.orig_gpr2)
-		mask = 3;
-#endif
-	if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
-		return -EIO;
+	addr_t offset, tmp;
 
 	if (addr < (addr_t) &dummy->regs.acrs) {
 		/*
@@ -197,24 +190,18 @@
 	} else
 		tmp = 0;
 
-	return put_user(tmp, (addr_t __user *) data);
+	return tmp;
 }
 
-/*
- * Write a word to the user area of a process at location addr. This
- * operation does have an additional problem compared to peek_user.
- * Stores to the program status word and on the floating point
- * control register needs to get checked for validity.
- */
 static int
-poke_user(struct task_struct *child, addr_t addr, addr_t data)
+peek_user(struct task_struct *child, addr_t addr, addr_t data)
 {
 	struct user *dummy = NULL;
-	addr_t offset, mask;
+	addr_t tmp, mask;
 
 	/*
 	 * Stupid gdb peeks/pokes the access registers in 64 bit with
-	 * an alignment of 4. Programmers from hell indeed...
+	 * an alignment of 4. Programmers from hell...
 	 */
 	mask = __ADDR_MASK;
 #ifdef CONFIG_64BIT
@@ -225,6 +212,21 @@
 	if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
 		return -EIO;
 
+	tmp = __peek_user(child, addr);
+	return put_user(tmp, (addr_t __user *) data);
+}
+
+/*
+ * Write a word to the user area of a process at location addr. This
+ * operation does have an additional problem compared to peek_user.
+ * Stores to the program status word and on the floating point
+ * control register needs to get checked for validity.
+ */
+static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+	struct user *dummy = NULL;
+	addr_t offset;
+
 	if (addr < (addr_t) &dummy->regs.acrs) {
 		/*
 		 * psw and gprs are stored on the stack
@@ -292,6 +294,28 @@
 	return 0;
 }
 
+static int
+poke_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+	struct user *dummy = NULL;
+	addr_t mask;
+
+	/*
+	 * Stupid gdb peeks/pokes the access registers in 64 bit with
+	 * an alignment of 4. Programmers from hell indeed...
+	 */
+	mask = __ADDR_MASK;
+#ifdef CONFIG_64BIT
+	if (addr >= (addr_t) &dummy->regs.acrs &&
+	    addr < (addr_t) &dummy->regs.orig_gpr2)
+		mask = 3;
+#endif
+	if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
+		return -EIO;
+
+	return __poke_user(child, addr, data);
+}
+
 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
 	ptrace_area parea; 
@@ -367,18 +391,13 @@
 /*
  * Same as peek_user but for a 31 bit program.
  */
-static int
-peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
+static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
 {
 	struct user32 *dummy32 = NULL;
 	per_struct32 *dummy_per32 = NULL;
 	addr_t offset;
 	__u32 tmp;
 
-	if (!test_thread_flag(TIF_31BIT) ||
-	    (addr & 3) || addr > sizeof(struct user) - 3)
-		return -EIO;
-
 	if (addr < (addr_t) &dummy32->regs.acrs) {
 		/*
 		 * psw and gprs are stored on the stack
@@ -435,25 +454,32 @@
 	} else
 		tmp = 0;
 
+	return tmp;
+}
+
+static int peek_user_compat(struct task_struct *child,
+			    addr_t addr, addr_t data)
+{
+	__u32 tmp;
+
+	if (!test_thread_flag(TIF_31BIT) ||
+	    (addr & 3) || addr > sizeof(struct user) - 3)
+		return -EIO;
+
+	tmp = __peek_user_compat(child, addr);
 	return put_user(tmp, (__u32 __user *) data);
 }
 
 /*
  * Same as poke_user but for a 31 bit program.
  */
-static int
-poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
+static int __poke_user_compat(struct task_struct *child,
+			      addr_t addr, addr_t data)
 {
 	struct user32 *dummy32 = NULL;
 	per_struct32 *dummy_per32 = NULL;
+	__u32 tmp = (__u32) data;
 	addr_t offset;
-	__u32 tmp;
-
-	if (!test_thread_flag(TIF_31BIT) ||
-	    (addr & 3) || addr > sizeof(struct user32) - 3)
-		return -EIO;
-
-	tmp = (__u32) data;
 
 	if (addr < (addr_t) &dummy32->regs.acrs) {
 		/*
@@ -528,6 +554,16 @@
 	return 0;
 }
 
+static int poke_user_compat(struct task_struct *child,
+			    addr_t addr, addr_t data)
+{
+	if (!test_thread_flag(TIF_31BIT) ||
+	    (addr & 3) || addr > sizeof(struct user32) - 3)
+		return -EIO;
+
+	return __poke_user_compat(child, addr, data);
+}
+
 long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 			compat_ulong_t caddr, compat_ulong_t cdata)
 {
@@ -539,11 +575,11 @@
 	switch (request) {
 	case PTRACE_PEEKUSR:
 		/* read the word at location addr in the USER area. */
-		return peek_user_emu31(child, addr, data);
+		return peek_user_compat(child, addr, data);
 
 	case PTRACE_POKEUSR:
 		/* write the word at location addr in the USER area */
-		return poke_user_emu31(child, addr, data);
+		return poke_user_compat(child, addr, data);
 
 	case PTRACE_PEEKUSR_AREA:
 	case PTRACE_POKEUSR_AREA:
@@ -555,13 +591,13 @@
 		copied = 0;
 		while (copied < parea.len) {
 			if (request == PTRACE_PEEKUSR_AREA)
-				ret = peek_user_emu31(child, addr, data);
+				ret = peek_user_compat(child, addr, data);
 			else {
 				__u32 utmp;
 				if (get_user(utmp,
 					     (__u32 __force __user *) data))
 					return -EFAULT;
-				ret = poke_user_emu31(child, addr, utmp);
+				ret = poke_user_compat(child, addr, utmp);
 			}
 			if (ret)
 				return ret;
@@ -610,3 +646,240 @@
 				    regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
 				    regs->gprs[4], regs->gprs[5]);
 }
+
+/*
+ * user_regset definitions.
+ */
+
+static int s390_regs_get(struct task_struct *target,
+			 const struct user_regset *regset,
+			 unsigned int pos, unsigned int count,
+			 void *kbuf, void __user *ubuf)
+{
+	if (target == current)
+		save_access_regs(target->thread.acrs);
+
+	if (kbuf) {
+		unsigned long *k = kbuf;
+		while (count > 0) {
+			*k++ = __peek_user(target, pos);
+			count -= sizeof(*k);
+			pos += sizeof(*k);
+		}
+	} else {
+		unsigned long __user *u = ubuf;
+		while (count > 0) {
+			if (__put_user(__peek_user(target, pos), u++))
+				return -EFAULT;
+			count -= sizeof(*u);
+			pos += sizeof(*u);
+		}
+	}
+	return 0;
+}
+
+static int s390_regs_set(struct task_struct *target,
+			 const struct user_regset *regset,
+			 unsigned int pos, unsigned int count,
+			 const void *kbuf, const void __user *ubuf)
+{
+	int rc = 0;
+
+	if (target == current)
+		save_access_regs(target->thread.acrs);
+
+	if (kbuf) {
+		const unsigned long *k = kbuf;
+		while (count > 0 && !rc) {
+			rc = __poke_user(target, pos, *k++);
+			count -= sizeof(*k);
+			pos += sizeof(*k);
+		}
+	} else {
+		const unsigned long  __user *u = ubuf;
+		while (count > 0 && !rc) {
+			unsigned long word;
+			rc = __get_user(word, u++);
+			if (rc)
+				break;
+			rc = __poke_user(target, pos, word);
+			count -= sizeof(*u);
+			pos += sizeof(*u);
+		}
+	}
+
+	if (rc == 0 && target == current)
+		restore_access_regs(target->thread.acrs);
+
+	return rc;
+}
+
+static int s390_fpregs_get(struct task_struct *target,
+			   const struct user_regset *regset, unsigned int pos,
+			   unsigned int count, void *kbuf, void __user *ubuf)
+{
+	if (target == current)
+		save_fp_regs(&target->thread.fp_regs);
+
+	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+				   &target->thread.fp_regs, 0, -1);
+}
+
+static int s390_fpregs_set(struct task_struct *target,
+			   const struct user_regset *regset, unsigned int pos,
+			   unsigned int count, const void *kbuf,
+			   const void __user *ubuf)
+{
+	int rc = 0;
+
+	if (target == current)
+		save_fp_regs(&target->thread.fp_regs);
+
+	/* If setting FPC, must validate it first. */
+	if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
+		u32 fpc[2] = { target->thread.fp_regs.fpc, 0 };
+		rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc,
+					0, offsetof(s390_fp_regs, fprs));
+		if (rc)
+			return rc;
+		if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0)
+			return -EINVAL;
+		target->thread.fp_regs.fpc = fpc[0];
+	}
+
+	if (rc == 0 && count > 0)
+		rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+					target->thread.fp_regs.fprs,
+					offsetof(s390_fp_regs, fprs), -1);
+
+	if (rc == 0 && target == current)
+		restore_fp_regs(&target->thread.fp_regs);
+
+	return rc;
+}
+
+static const struct user_regset s390_regsets[] = {
+	[REGSET_GENERAL] = {
+		.core_note_type = NT_PRSTATUS,
+		.n = sizeof(s390_regs) / sizeof(long),
+		.size = sizeof(long),
+		.align = sizeof(long),
+		.get = s390_regs_get,
+		.set = s390_regs_set,
+	},
+	[REGSET_FP] = {
+		.core_note_type = NT_PRFPREG,
+		.n = sizeof(s390_fp_regs) / sizeof(long),
+		.size = sizeof(long),
+		.align = sizeof(long),
+		.get = s390_fpregs_get,
+		.set = s390_fpregs_set,
+	},
+};
+
+static const struct user_regset_view user_s390_view = {
+	.name = UTS_MACHINE,
+	.e_machine = EM_S390,
+	.regsets = s390_regsets,
+	.n = ARRAY_SIZE(s390_regsets)
+};
+
+#ifdef CONFIG_COMPAT
+static int s390_compat_regs_get(struct task_struct *target,
+				const struct user_regset *regset,
+				unsigned int pos, unsigned int count,
+				void *kbuf, void __user *ubuf)
+{
+	if (target == current)
+		save_access_regs(target->thread.acrs);
+
+	if (kbuf) {
+		compat_ulong_t *k = kbuf;
+		while (count > 0) {
+			*k++ = __peek_user_compat(target, pos);
+			count -= sizeof(*k);
+			pos += sizeof(*k);
+		}
+	} else {
+		compat_ulong_t __user *u = ubuf;
+		while (count > 0) {
+			if (__put_user(__peek_user_compat(target, pos), u++))
+				return -EFAULT;
+			count -= sizeof(*u);
+			pos += sizeof(*u);
+		}
+	}
+	return 0;
+}
+
+static int s390_compat_regs_set(struct task_struct *target,
+				const struct user_regset *regset,
+				unsigned int pos, unsigned int count,
+				const void *kbuf, const void __user *ubuf)
+{
+	int rc = 0;
+
+	if (target == current)
+		save_access_regs(target->thread.acrs);
+
+	if (kbuf) {
+		const compat_ulong_t *k = kbuf;
+		while (count > 0 && !rc) {
+			rc = __poke_user_compat(target, pos, *k++);
+			count -= sizeof(*k);
+			pos += sizeof(*k);
+		}
+	} else {
+		const compat_ulong_t  __user *u = ubuf;
+		while (count > 0 && !rc) {
+			compat_ulong_t word;
+			rc = __get_user(word, u++);
+			if (rc)
+				break;
+			rc = __poke_user_compat(target, pos, word);
+			count -= sizeof(*u);
+			pos += sizeof(*u);
+		}
+	}
+
+	if (rc == 0 && target == current)
+		restore_access_regs(target->thread.acrs);
+
+	return rc;
+}
+
+static const struct user_regset s390_compat_regsets[] = {
+	[REGSET_GENERAL] = {
+		.core_note_type = NT_PRSTATUS,
+		.n = sizeof(s390_compat_regs) / sizeof(compat_long_t),
+		.size = sizeof(compat_long_t),
+		.align = sizeof(compat_long_t),
+		.get = s390_compat_regs_get,
+		.set = s390_compat_regs_set,
+	},
+	[REGSET_FP] = {
+		.core_note_type = NT_PRFPREG,
+		.n = sizeof(s390_fp_regs) / sizeof(compat_long_t),
+		.size = sizeof(compat_long_t),
+		.align = sizeof(compat_long_t),
+		.get = s390_fpregs_get,
+		.set = s390_fpregs_set,
+	},
+};
+
+static const struct user_regset_view user_s390_compat_view = {
+	.name = "s390",
+	.e_machine = EM_S390,
+	.regsets = s390_compat_regsets,
+	.n = ARRAY_SIZE(s390_compat_regsets)
+};
+#endif
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+#ifdef CONFIG_COMPAT
+	if (test_tsk_thread_flag(task, TIF_31BIT))
+		return &user_s390_compat_view;
+#endif
+	return &user_s390_view;
+}
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 2bc70b6..b358e18 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -77,7 +77,7 @@
 unsigned long elf_hwcap = 0;
 char elf_platform[ELF_PLATFORM_SIZE];
 
-struct mem_chunk __meminitdata memory_chunk[MEMORY_CHUNKS];
+struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS];
 volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
 static unsigned long __initdata memory_end;
 
@@ -205,12 +205,6 @@
 			SET_CONSOLE_SCLP;
 #endif
 		}
-        } else if (MACHINE_IS_P390) {
-#if defined(CONFIG_TN3215_CONSOLE)
-		SET_CONSOLE_3215;
-#elif defined(CONFIG_TN3270_CONSOLE)
-		SET_CONSOLE_3270;
-#endif
 	} else {
 #if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
 		SET_CONSOLE_SCLP;
@@ -221,18 +215,17 @@
 #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
 static void __init setup_zfcpdump(unsigned int console_devno)
 {
-	static char str[64];
+	static char str[41];
 
 	if (ipl_info.type != IPL_TYPE_FCP_DUMP)
 		return;
 	if (console_devno != -1)
-		sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x",
+		sprintf(str, " cio_ignore=all,!0.0.%04x,!0.0.%04x",
 			ipl_info.data.fcp.dev_id.devno, console_devno);
 	else
-		sprintf(str, "cio_ignore=all,!0.0.%04x",
+		sprintf(str, " cio_ignore=all,!0.0.%04x",
 			ipl_info.data.fcp.dev_id.devno);
-	strcat(COMMAND_LINE, " ");
-	strcat(COMMAND_LINE, str);
+	strcat(boot_command_line, str);
 	console_loglevel = 2;
 }
 #else
@@ -289,32 +282,6 @@
 }
 early_param("mem", early_parse_mem);
 
-/*
- * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes
- */
-static int __init early_parse_ipldelay(char *p)
-{
-	unsigned long delay = 0;
-
-	delay = simple_strtoul(p, &p, 0);
-
-	switch (*p) {
-	case 's':
-	case 'S':
-		delay *= 1000000;
-		break;
-	case 'm':
-	case 'M':
-		delay *= 60 * 1000000;
-	}
-
-	/* now wait for the requested amount of time */
-	udelay(delay);
-
-	return 0;
-}
-early_param("ipldelay", early_parse_ipldelay);
-
 #ifdef CONFIG_S390_SWITCH_AMODE
 #ifdef CONFIG_PGSTE
 unsigned int switch_amode = 1;
@@ -804,11 +771,9 @@
 		printk("We are running native (64 bit mode)\n");
 #endif /* CONFIG_64BIT */
 
-	/* Save unparsed command line copy for /proc/cmdline */
-	strlcpy(boot_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
-
-	*cmdline_p = COMMAND_LINE;
-	*(*cmdline_p + COMMAND_LINE_SIZE - 1) = '\0';
+	/* Have one command line that is parsed and saved in /proc/cmdline */
+	/* boot_command_line has been already set up in early.c */
+	*cmdline_p = boot_command_line;
 
         ROOT_DEV = Root_RAM0;
 
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 7aec676..7418beb 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -3,7 +3,7 @@
  *    Time of day based timer functions.
  *
  *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Copyright IBM Corp. 1999, 2008
  *    Author(s): Hartmut Penner (hp@de.ibm.com),
  *               Martin Schwidefsky (schwidefsky@de.ibm.com),
  *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
@@ -31,6 +31,7 @@
 #include <linux/notifier.h>
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
+#include <linux/bootmem.h>
 #include <asm/uaccess.h>
 #include <asm/delay.h>
 #include <asm/s390_ext.h>
@@ -162,7 +163,7 @@
 	/* Enable clock comparator timer interrupt. */
 	__ctl_set_bit(0,11);
 
-	/* Always allow ETR external interrupts, even without an ETR. */
+	/* Always allow the timing alert external interrupt. */
 	__ctl_set_bit(0, 4);
 }
 
@@ -170,8 +171,21 @@
 {
 }
 
+static void etr_timing_alert(struct etr_irq_parm *);
+static void stp_timing_alert(struct stp_irq_parm *);
+
+static void timing_alert_interrupt(__u16 code)
+{
+	if (S390_lowcore.ext_params & 0x00c40000)
+		etr_timing_alert((struct etr_irq_parm *)
+				 &S390_lowcore.ext_params);
+	if (S390_lowcore.ext_params & 0x00038000)
+		stp_timing_alert((struct stp_irq_parm *)
+				 &S390_lowcore.ext_params);
+}
+
 static void etr_reset(void);
-static void etr_ext_handler(__u16);
+static void stp_reset(void);
 
 /*
  * Get the TOD clock running.
@@ -181,6 +195,7 @@
 	u64 time;
 
 	etr_reset();
+	stp_reset();
 	if (store_clock(&time) == 0)
 		return time;
 	/* TOD clock not running. Set the clock to Unix Epoch. */
@@ -231,8 +246,9 @@
 	if (clocksource_register(&clocksource_tod) != 0)
 		panic("Could not register TOD clock source");
 
-	/* request the etr external interrupt */
-	if (register_early_external_interrupt(0x1406, etr_ext_handler,
+	/* request the timing alert external interrupt */
+	if (register_early_external_interrupt(0x1406,
+					      timing_alert_interrupt,
 					      &ext_int_etr_cc) != 0)
 		panic("Couldn't request external interrupt 0x1406");
 
@@ -245,10 +261,112 @@
 }
 
 /*
+ * The time is "clock". old is what we think the time is.
+ * Adjust the value by a multiple of jiffies and add the delta to ntp.
+ * "delay" is an approximation how long the synchronization took. If
+ * the time correction is positive, then "delay" is subtracted from
+ * the time difference and only the remaining part is passed to ntp.
+ */
+static unsigned long long adjust_time(unsigned long long old,
+				      unsigned long long clock,
+				      unsigned long long delay)
+{
+	unsigned long long delta, ticks;
+	struct timex adjust;
+
+	if (clock > old) {
+		/* It is later than we thought. */
+		delta = ticks = clock - old;
+		delta = ticks = (delta < delay) ? 0 : delta - delay;
+		delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
+		adjust.offset = ticks * (1000000 / HZ);
+	} else {
+		/* It is earlier than we thought. */
+		delta = ticks = old - clock;
+		delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
+		delta = -delta;
+		adjust.offset = -ticks * (1000000 / HZ);
+	}
+	jiffies_timer_cc += delta;
+	if (adjust.offset != 0) {
+		printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n",
+		       adjust.offset);
+		adjust.modes = ADJ_OFFSET_SINGLESHOT;
+		do_adjtimex(&adjust);
+	}
+	return delta;
+}
+
+static DEFINE_PER_CPU(atomic_t, clock_sync_word);
+static unsigned long clock_sync_flags;
+
+#define CLOCK_SYNC_HAS_ETR	0
+#define CLOCK_SYNC_HAS_STP	1
+#define CLOCK_SYNC_ETR		2
+#define CLOCK_SYNC_STP		3
+
+/*
+ * The synchronous get_clock function. It will write the current clock
+ * value to the clock pointer and return 0 if the clock is in sync with
+ * the external time source. If the clock mode is local it will return
+ * -ENOSYS and -EAGAIN if the clock is not in sync with the external
+ * reference.
+ */
+int get_sync_clock(unsigned long long *clock)
+{
+	atomic_t *sw_ptr;
+	unsigned int sw0, sw1;
+
+	sw_ptr = &get_cpu_var(clock_sync_word);
+	sw0 = atomic_read(sw_ptr);
+	*clock = get_clock();
+	sw1 = atomic_read(sw_ptr);
+	put_cpu_var(clock_sync_sync);
+	if (sw0 == sw1 && (sw0 & 0x80000000U))
+		/* Success: time is in sync. */
+		return 0;
+	if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) &&
+	    !test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+		return -ENOSYS;
+	if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) &&
+	    !test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
+		return -EACCES;
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(get_sync_clock);
+
+/*
+ * Make get_sync_clock return -EAGAIN.
+ */
+static void disable_sync_clock(void *dummy)
+{
+	atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
+	/*
+	 * Clear the in-sync bit 2^31. All get_sync_clock calls will
+	 * fail until the sync bit is turned back on. In addition
+	 * increase the "sequence" counter to avoid the race of an
+	 * etr event and the complete recovery against get_sync_clock.
+	 */
+	atomic_clear_mask(0x80000000, sw_ptr);
+	atomic_inc(sw_ptr);
+}
+
+/*
+ * Make get_sync_clock return 0 again.
+ * Needs to be called from a context disabled for preemption.
+ */
+static void enable_sync_clock(void)
+{
+	atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
+	atomic_set_mask(0x80000000, sw_ptr);
+}
+
+/*
  * External Time Reference (ETR) code.
  */
 static int etr_port0_online;
 static int etr_port1_online;
+static int etr_steai_available;
 
 static int __init early_parse_etr(char *p)
 {
@@ -273,12 +391,6 @@
 	ETR_EVENT_UPDATE,
 };
 
-enum etr_flags {
-	ETR_FLAG_ENOSYS,
-	ETR_FLAG_EACCES,
-	ETR_FLAG_STEAI,
-};
-
 /*
  * Valid bit combinations of the eacr register are (x = don't care):
  * e0 e1 dp p0 p1 ea es sl
@@ -305,74 +417,18 @@
  */
 static struct etr_eacr etr_eacr;
 static u64 etr_tolec;			/* time of last eacr update */
-static unsigned long etr_flags;
 static struct etr_aib etr_port0;
 static int etr_port0_uptodate;
 static struct etr_aib etr_port1;
 static int etr_port1_uptodate;
 static unsigned long etr_events;
 static struct timer_list etr_timer;
-static DEFINE_PER_CPU(atomic_t, etr_sync_word);
 
 static void etr_timeout(unsigned long dummy);
 static void etr_work_fn(struct work_struct *work);
 static DECLARE_WORK(etr_work, etr_work_fn);
 
 /*
- * The etr get_clock function. It will write the current clock value
- * to the clock pointer and return 0 if the clock is in sync with the
- * external time source. If the clock mode is local it will return
- * -ENOSYS and -EAGAIN if the clock is not in sync with the external
- * reference. This function is what ETR is all about..
- */
-int get_sync_clock(unsigned long long *clock)
-{
-	atomic_t *sw_ptr;
-	unsigned int sw0, sw1;
-
-	sw_ptr = &get_cpu_var(etr_sync_word);
-	sw0 = atomic_read(sw_ptr);
-	*clock = get_clock();
-	sw1 = atomic_read(sw_ptr);
-	put_cpu_var(etr_sync_sync);
-	if (sw0 == sw1 && (sw0 & 0x80000000U))
-		/* Success: time is in sync. */
-		return 0;
-	if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))
-		return -ENOSYS;
-	if (test_bit(ETR_FLAG_EACCES, &etr_flags))
-		return -EACCES;
-	return -EAGAIN;
-}
-EXPORT_SYMBOL(get_sync_clock);
-
-/*
- * Make get_sync_clock return -EAGAIN.
- */
-static void etr_disable_sync_clock(void *dummy)
-{
-	atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word);
-	/*
-	 * Clear the in-sync bit 2^31. All get_sync_clock calls will
-	 * fail until the sync bit is turned back on. In addition
-	 * increase the "sequence" counter to avoid the race of an
-	 * etr event and the complete recovery against get_sync_clock.
-	 */
-	atomic_clear_mask(0x80000000, sw_ptr);
-	atomic_inc(sw_ptr);
-}
-
-/*
- * Make get_sync_clock return 0 again.
- * Needs to be called from a context disabled for preemption.
- */
-static void etr_enable_sync_clock(void)
-{
-	atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word);
-	atomic_set_mask(0x80000000, sw_ptr);
-}
-
-/*
  * Reset ETR attachment.
  */
 static void etr_reset(void)
@@ -381,15 +437,13 @@
 		.e0 = 0, .e1 = 0, ._pad0 = 4, .dp = 0,
 		.p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0,
 		.es = 0, .sl = 0 };
-	if (etr_setr(&etr_eacr) == 0)
+	if (etr_setr(&etr_eacr) == 0) {
 		etr_tolec = get_clock();
-	else {
-		set_bit(ETR_FLAG_ENOSYS, &etr_flags);
-		if (etr_port0_online || etr_port1_online) {
-			printk(KERN_WARNING "Running on non ETR capable "
-			       "machine, only local mode available.\n");
-			etr_port0_online = etr_port1_online = 0;
-		}
+		set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags);
+	} else if (etr_port0_online || etr_port1_online) {
+		printk(KERN_WARNING "Running on non ETR capable "
+		       "machine, only local mode available.\n");
+		etr_port0_online = etr_port1_online = 0;
 	}
 }
 
@@ -397,14 +451,12 @@
 {
 	struct etr_aib aib;
 
-	if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))
+	if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
 		return 0;
 	/* Check if this machine has the steai instruction. */
 	if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0)
-		set_bit(ETR_FLAG_STEAI, &etr_flags);
+		etr_steai_available = 1;
 	setup_timer(&etr_timer, etr_timeout, 0UL);
-	if (!etr_port0_online && !etr_port1_online)
-		set_bit(ETR_FLAG_EACCES, &etr_flags);
 	if (etr_port0_online) {
 		set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
 		schedule_work(&etr_work);
@@ -435,7 +487,8 @@
 {
 	if (!etr_eacr.sl)
 		return;
-	etr_disable_sync_clock(NULL);
+	if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+		disable_sync_clock(NULL);
 	set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
 	schedule_work(&etr_work);
 }
@@ -450,23 +503,21 @@
 {
 	if (!etr_eacr.es)
 		return;
-	etr_disable_sync_clock(NULL);
+	if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+		disable_sync_clock(NULL);
 	set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
 	schedule_work(&etr_work);
 }
 
 /*
- * ETR external interrupt. There are two causes:
+ * ETR timing alert. There are two causes:
  * 1) port state change, check the usability of the port
  * 2) port alert, one of the ETR-data-validity bits (v1-v2 bits of the
  *    sldr-status word) or ETR-data word 1 (edf1) or ETR-data word 3 (edf3)
  *    or ETR-data word 4 (edf4) has changed.
  */
-static void etr_ext_handler(__u16 code)
+static void etr_timing_alert(struct etr_irq_parm *intparm)
 {
-	struct etr_interruption_parameter *intparm =
-		(struct etr_interruption_parameter *) &S390_lowcore.ext_params;
-
 	if (intparm->pc0)
 		/* ETR port 0 state change. */
 		set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
@@ -591,58 +642,23 @@
 	return 1;
 }
 
-/*
- * The time is "clock". old is what we think the time is.
- * Adjust the value by a multiple of jiffies and add the delta to ntp.
- * "delay" is an approximation how long the synchronization took. If
- * the time correction is positive, then "delay" is subtracted from
- * the time difference and only the remaining part is passed to ntp.
- */
-static unsigned long long etr_adjust_time(unsigned long long old,
-					  unsigned long long clock,
-					  unsigned long long delay)
-{
-	unsigned long long delta, ticks;
-	struct timex adjust;
-
-	if (clock > old) {
-		/* It is later than we thought. */
-		delta = ticks = clock - old;
-		delta = ticks = (delta < delay) ? 0 : delta - delay;
-		delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
-		adjust.offset = ticks * (1000000 / HZ);
-	} else {
-		/* It is earlier than we thought. */
-		delta = ticks = old - clock;
-		delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
-		delta = -delta;
-		adjust.offset = -ticks * (1000000 / HZ);
-	}
-	jiffies_timer_cc += delta;
-	if (adjust.offset != 0) {
-		printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n",
-		       adjust.offset);
-		adjust.modes = ADJ_OFFSET_SINGLESHOT;
-		do_adjtimex(&adjust);
-	}
-	return delta;
-}
-
-static struct {
+struct clock_sync_data {
 	int in_sync;
 	unsigned long long fixup_cc;
-} etr_sync;
+};
 
-static void etr_sync_cpu_start(void *dummy)
+static void clock_sync_cpu_start(void *dummy)
 {
-	etr_enable_sync_clock();
+	struct clock_sync_data *sync = dummy;
+
+	enable_sync_clock();
 	/*
 	 * This looks like a busy wait loop but it isn't. etr_sync_cpus
 	 * is called on all other cpus while the TOD clocks is stopped.
 	 * __udelay will stop the cpu on an enabled wait psw until the
 	 * TOD is running again.
 	 */
-	while (etr_sync.in_sync == 0) {
+	while (sync->in_sync == 0) {
 		__udelay(1);
 		/*
 		 * A different cpu changes *in_sync. Therefore use
@@ -650,17 +666,17 @@
 		 */
 		barrier();
 	}
-	if (etr_sync.in_sync != 1)
+	if (sync->in_sync != 1)
 		/* Didn't work. Clear per-cpu in sync bit again. */
-		etr_disable_sync_clock(NULL);
+		disable_sync_clock(NULL);
 	/*
 	 * This round of TOD syncing is done. Set the clock comparator
 	 * to the next tick and let the processor continue.
 	 */
-	fixup_clock_comparator(etr_sync.fixup_cc);
+	fixup_clock_comparator(sync->fixup_cc);
 }
 
-static void etr_sync_cpu_end(void *dummy)
+static void clock_sync_cpu_end(void *dummy)
 {
 }
 
@@ -672,6 +688,7 @@
 static int etr_sync_clock(struct etr_aib *aib, int port)
 {
 	struct etr_aib *sync_port;
+	struct clock_sync_data etr_sync;
 	unsigned long long clock, old_clock, delay, delta;
 	int follows;
 	int rc;
@@ -690,9 +707,9 @@
 	 */
 	memset(&etr_sync, 0, sizeof(etr_sync));
 	preempt_disable();
-	smp_call_function(etr_sync_cpu_start, NULL, 0, 0);
+	smp_call_function(clock_sync_cpu_start, &etr_sync, 0, 0);
 	local_irq_disable();
-	etr_enable_sync_clock();
+	enable_sync_clock();
 
 	/* Set clock to next OTE. */
 	__ctl_set_bit(14, 21);
@@ -707,13 +724,13 @@
 		/* Adjust Linux timing variables. */
 		delay = (unsigned long long)
 			(aib->edf2.etv - sync_port->edf2.etv) << 32;
-		delta = etr_adjust_time(old_clock, clock, delay);
+		delta = adjust_time(old_clock, clock, delay);
 		etr_sync.fixup_cc = delta;
 		fixup_clock_comparator(delta);
 		/* Verify that the clock is properly set. */
 		if (!etr_aib_follows(sync_port, aib, port)) {
 			/* Didn't work. */
-			etr_disable_sync_clock(NULL);
+			disable_sync_clock(NULL);
 			etr_sync.in_sync = -EAGAIN;
 			rc = -EAGAIN;
 		} else {
@@ -724,12 +741,12 @@
 		/* Could not set the clock ?!? */
 		__ctl_clear_bit(0, 29);
 		__ctl_clear_bit(14, 21);
-		etr_disable_sync_clock(NULL);
+		disable_sync_clock(NULL);
 		etr_sync.in_sync = -EAGAIN;
 		rc = -EAGAIN;
 	}
 	local_irq_enable();
-	smp_call_function(etr_sync_cpu_end,NULL,0,0);
+	smp_call_function(clock_sync_cpu_end, NULL, 0, 0);
 	preempt_enable();
 	return rc;
 }
@@ -832,7 +849,7 @@
 	 * Do not try to get the alternate port aib if the clock
 	 * is not in sync yet.
 	 */
-	if (!eacr.es)
+	if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags) && !eacr.es)
 		return eacr;
 
 	/*
@@ -840,7 +857,7 @@
 	 * the other port immediately. If only stetr is available the
 	 * data-port bit toggle has to be used.
 	 */
-	if (test_bit(ETR_FLAG_STEAI, &etr_flags)) {
+	if (etr_steai_available) {
 		if (eacr.p0 && !etr_port0_uptodate) {
 			etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0);
 			etr_port0_uptodate = 1;
@@ -909,10 +926,10 @@
 	if (!eacr.ea) {
 		/* Both ports offline. Reset everything. */
 		eacr.dp = eacr.es = eacr.sl = 0;
-		on_each_cpu(etr_disable_sync_clock, NULL, 0, 1);
+		on_each_cpu(disable_sync_clock, NULL, 0, 1);
 		del_timer_sync(&etr_timer);
 		etr_update_eacr(eacr);
-		set_bit(ETR_FLAG_EACCES, &etr_flags);
+		clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
 		return;
 	}
 
@@ -953,7 +970,6 @@
 			eacr.e1 = 1;
 		sync_port = (etr_port0_uptodate &&
 			     etr_port_valid(&etr_port0, 0)) ? 0 : -1;
-		clear_bit(ETR_FLAG_EACCES, &etr_flags);
 	} else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) {
 		eacr.sl = 0;
 		eacr.e0 = 0;
@@ -962,7 +978,6 @@
 			eacr.es = 0;
 		sync_port = (etr_port1_uptodate &&
 			     etr_port_valid(&etr_port1, 1)) ? 1 : -1;
-		clear_bit(ETR_FLAG_EACCES, &etr_flags);
 	} else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) {
 		eacr.sl = 1;
 		eacr.e0 = 1;
@@ -976,7 +991,6 @@
 			eacr.e1 = 1;
 		sync_port = (etr_port0_uptodate &&
 			     etr_port_valid(&etr_port0, 0)) ? 0 : -1;
-		clear_bit(ETR_FLAG_EACCES, &etr_flags);
 	} else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) {
 		eacr.sl = 1;
 		eacr.e0 = 0;
@@ -985,19 +999,22 @@
 			eacr.es = 0;
 		sync_port = (etr_port1_uptodate &&
 			     etr_port_valid(&etr_port1, 1)) ? 1 : -1;
-		clear_bit(ETR_FLAG_EACCES, &etr_flags);
 	} else {
 		/* Both ports not usable. */
 		eacr.es = eacr.sl = 0;
 		sync_port = -1;
-		set_bit(ETR_FLAG_EACCES, &etr_flags);
+		clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
 	}
 
+	if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+		eacr.es = 0;
+
 	/*
 	 * If the clock is in sync just update the eacr and return.
 	 * If there is no valid sync port wait for a port update.
 	 */
-	if (eacr.es || sync_port < 0) {
+	if (test_bit(CLOCK_SYNC_STP, &clock_sync_flags) ||
+	    eacr.es || sync_port < 0) {
 		etr_update_eacr(eacr);
 		etr_set_tolec_timeout(now);
 		return;
@@ -1018,11 +1035,13 @@
 	 * and set up a timer to try again after 0.5 seconds
 	 */
 	etr_update_eacr(eacr);
+	set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
 	if (now < etr_tolec + (1600000 << 12) ||
 	    etr_sync_clock(&aib, sync_port) != 0) {
 		/* Sync failed. Try again in 1/2 second. */
 		eacr.es = 0;
 		etr_update_eacr(eacr);
+		clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
 		etr_set_sync_timeout();
 	} else
 		etr_set_tolec_timeout(now);
@@ -1097,8 +1116,8 @@
 	value = simple_strtoul(buf, NULL, 0);
 	if (value != 0 && value != 1)
 		return -EINVAL;
-	if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))
-		return -ENOSYS;
+	if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
+		return -EOPNOTSUPP;
 	if (dev == &etr_port0_dev) {
 		if (etr_port0_online == value)
 			return count;	/* Nothing to do. */
@@ -1292,3 +1311,318 @@
 }
 
 device_initcall(etr_init_sysfs);
+
+/*
+ * Server Time Protocol (STP) code.
+ */
+static int stp_online;
+static struct stp_sstpi stp_info;
+static void *stp_page;
+
+static void stp_work_fn(struct work_struct *work);
+static DECLARE_WORK(stp_work, stp_work_fn);
+
+static int __init early_parse_stp(char *p)
+{
+	if (strncmp(p, "off", 3) == 0)
+		stp_online = 0;
+	else if (strncmp(p, "on", 2) == 0)
+		stp_online = 1;
+	return 0;
+}
+early_param("stp", early_parse_stp);
+
+/*
+ * Reset STP attachment.
+ */
+static void stp_reset(void)
+{
+	int rc;
+
+	stp_page = alloc_bootmem_pages(PAGE_SIZE);
+	rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+	if (rc == 1)
+		set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags);
+	else if (stp_online) {
+		printk(KERN_WARNING "Running on non STP capable machine.\n");
+		free_bootmem((unsigned long) stp_page, PAGE_SIZE);
+		stp_page = NULL;
+		stp_online = 0;
+	}
+}
+
+static int __init stp_init(void)
+{
+	if (test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags) && stp_online)
+		schedule_work(&stp_work);
+	return 0;
+}
+
+arch_initcall(stp_init);
+
+/*
+ * STP timing alert. There are three causes:
+ * 1) timing status change
+ * 2) link availability change
+ * 3) time control parameter change
+ * In all three cases we are only interested in the clock source state.
+ * If a STP clock source is now available use it.
+ */
+static void stp_timing_alert(struct stp_irq_parm *intparm)
+{
+	if (intparm->tsc || intparm->lac || intparm->tcpc)
+		schedule_work(&stp_work);
+}
+
+/*
+ * STP sync check machine check. This is called when the timing state
+ * changes from the synchronized state to the unsynchronized state.
+ * After a STP sync check the clock is not in sync. The machine check
+ * is broadcasted to all cpus at the same time.
+ */
+void stp_sync_check(void)
+{
+	if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
+		return;
+	disable_sync_clock(NULL);
+	schedule_work(&stp_work);
+}
+
+/*
+ * STP island condition machine check. This is called when an attached
+ * server  attempts to communicate over an STP link and the servers
+ * have matching CTN ids and have a valid stratum-1 configuration
+ * but the configurations do not match.
+ */
+void stp_island_check(void)
+{
+	if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
+		return;
+	disable_sync_clock(NULL);
+	schedule_work(&stp_work);
+}
+
+/*
+ * STP tasklet. Check for the STP state and take over the clock
+ * synchronization if the STP clock source is usable.
+ */
+static void stp_work_fn(struct work_struct *work)
+{
+	struct clock_sync_data stp_sync;
+	unsigned long long old_clock, delta;
+	int rc;
+
+	if (!stp_online) {
+		chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+		return;
+	}
+
+	rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0);
+	if (rc)
+		return;
+
+	rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
+	if (rc || stp_info.c == 0)
+		return;
+
+	/*
+	 * Catch all other cpus and make them wait until we have
+	 * successfully synced the clock. smp_call_function will
+	 * return after all other cpus are in clock_sync_cpu_start.
+	 */
+	memset(&stp_sync, 0, sizeof(stp_sync));
+	preempt_disable();
+	smp_call_function(clock_sync_cpu_start, &stp_sync, 0, 0);
+	local_irq_disable();
+	enable_sync_clock();
+
+	set_bit(CLOCK_SYNC_STP, &clock_sync_flags);
+	if (test_and_clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+		schedule_work(&etr_work);
+
+	rc = 0;
+	if (stp_info.todoff[0] || stp_info.todoff[1] ||
+	    stp_info.todoff[2] || stp_info.todoff[3] ||
+	    stp_info.tmd != 2) {
+		old_clock = get_clock();
+		rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0);
+		if (rc == 0) {
+			delta = adjust_time(old_clock, get_clock(), 0);
+			fixup_clock_comparator(delta);
+			rc = chsc_sstpi(stp_page, &stp_info,
+					sizeof(struct stp_sstpi));
+			if (rc == 0 && stp_info.tmd != 2)
+				rc = -EAGAIN;
+		}
+	}
+	if (rc) {
+		disable_sync_clock(NULL);
+		stp_sync.in_sync = -EAGAIN;
+		clear_bit(CLOCK_SYNC_STP, &clock_sync_flags);
+		if (etr_port0_online || etr_port1_online)
+			schedule_work(&etr_work);
+	} else
+		stp_sync.in_sync = 1;
+
+	local_irq_enable();
+	smp_call_function(clock_sync_cpu_end, NULL, 0, 0);
+	preempt_enable();
+}
+
+/*
+ * STP class sysfs interface functions
+ */
+static struct sysdev_class stp_sysclass = {
+	.name	= "stp",
+};
+
+static ssize_t stp_ctn_id_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online)
+		return -ENODATA;
+	return sprintf(buf, "%016llx\n",
+		       *(unsigned long long *) stp_info.ctnid);
+}
+
+static SYSDEV_CLASS_ATTR(ctn_id, 0400, stp_ctn_id_show, NULL);
+
+static ssize_t stp_ctn_type_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online)
+		return -ENODATA;
+	return sprintf(buf, "%i\n", stp_info.ctn);
+}
+
+static SYSDEV_CLASS_ATTR(ctn_type, 0400, stp_ctn_type_show, NULL);
+
+static ssize_t stp_dst_offset_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online || !(stp_info.vbits & 0x2000))
+		return -ENODATA;
+	return sprintf(buf, "%i\n", (int)(s16) stp_info.dsto);
+}
+
+static SYSDEV_CLASS_ATTR(dst_offset, 0400, stp_dst_offset_show, NULL);
+
+static ssize_t stp_leap_seconds_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online || !(stp_info.vbits & 0x8000))
+		return -ENODATA;
+	return sprintf(buf, "%i\n", (int)(s16) stp_info.leaps);
+}
+
+static SYSDEV_CLASS_ATTR(leap_seconds, 0400, stp_leap_seconds_show, NULL);
+
+static ssize_t stp_stratum_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online)
+		return -ENODATA;
+	return sprintf(buf, "%i\n", (int)(s16) stp_info.stratum);
+}
+
+static SYSDEV_CLASS_ATTR(stratum, 0400, stp_stratum_show, NULL);
+
+static ssize_t stp_time_offset_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online || !(stp_info.vbits & 0x0800))
+		return -ENODATA;
+	return sprintf(buf, "%i\n", (int) stp_info.tto);
+}
+
+static SYSDEV_CLASS_ATTR(time_offset, 0400, stp_time_offset_show, NULL);
+
+static ssize_t stp_time_zone_offset_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online || !(stp_info.vbits & 0x4000))
+		return -ENODATA;
+	return sprintf(buf, "%i\n", (int)(s16) stp_info.tzo);
+}
+
+static SYSDEV_CLASS_ATTR(time_zone_offset, 0400,
+			 stp_time_zone_offset_show, NULL);
+
+static ssize_t stp_timing_mode_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online)
+		return -ENODATA;
+	return sprintf(buf, "%i\n", stp_info.tmd);
+}
+
+static SYSDEV_CLASS_ATTR(timing_mode, 0400, stp_timing_mode_show, NULL);
+
+static ssize_t stp_timing_state_show(struct sysdev_class *class, char *buf)
+{
+	if (!stp_online)
+		return -ENODATA;
+	return sprintf(buf, "%i\n", stp_info.tst);
+}
+
+static SYSDEV_CLASS_ATTR(timing_state, 0400, stp_timing_state_show, NULL);
+
+static ssize_t stp_online_show(struct sysdev_class *class, char *buf)
+{
+	return sprintf(buf, "%i\n", stp_online);
+}
+
+static ssize_t stp_online_store(struct sysdev_class *class,
+				const char *buf, size_t count)
+{
+	unsigned int value;
+
+	value = simple_strtoul(buf, NULL, 0);
+	if (value != 0 && value != 1)
+		return -EINVAL;
+	if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+		return -EOPNOTSUPP;
+	stp_online = value;
+	schedule_work(&stp_work);
+	return count;
+}
+
+/*
+ * Can't use SYSDEV_CLASS_ATTR because the attribute should be named
+ * stp/online but attr_online already exists in this file ..
+ */
+static struct sysdev_class_attribute attr_stp_online = {
+	.attr = { .name = "online", .mode = 0600 },
+	.show	= stp_online_show,
+	.store	= stp_online_store,
+};
+
+static struct sysdev_class_attribute *stp_attributes[] = {
+	&attr_ctn_id,
+	&attr_ctn_type,
+	&attr_dst_offset,
+	&attr_leap_seconds,
+	&attr_stp_online,
+	&attr_stratum,
+	&attr_time_offset,
+	&attr_time_zone_offset,
+	&attr_timing_mode,
+	&attr_timing_state,
+	NULL
+};
+
+static int __init stp_init_sysfs(void)
+{
+	struct sysdev_class_attribute **attr;
+	int rc;
+
+	rc = sysdev_class_register(&stp_sysclass);
+	if (rc)
+		goto out;
+	for (attr = stp_attributes; *attr; attr++) {
+		rc = sysdev_class_create_file(&stp_sysclass, *attr);
+		if (rc)
+			goto out_unreg;
+	}
+	return 0;
+out_unreg:
+	for (; attr >= stp_attributes; attr--)
+		sysdev_class_remove_file(&stp_sysclass, *attr);
+	sysdev_class_unregister(&stp_sysclass);
+out:
+	return rc;
+}
+
+device_initcall(stp_init_sysfs);
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index 661a072..212d618 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -313,8 +313,6 @@
 		machine_has_topology_irq = 1;
 
 	tl_info = alloc_bootmem_pages(PAGE_SIZE);
-	if (!tl_info)
-		goto error;
 	info = tl_info;
 	stsi(info, 15, 1, 2);
 
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index ca90ee3..0fa5dc5 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -136,7 +136,7 @@
 }
 #endif
 
-static void start_cpu_timer(void)
+void vtime_start_cpu_timer(void)
 {
 	struct vtimer_queue *vt_list;
 
@@ -150,7 +150,7 @@
 		set_vtimer(vt_list->idle);
 }
 
-static void stop_cpu_timer(void)
+void vtime_stop_cpu_timer(void)
 {
 	struct vtimer_queue *vt_list;
 
@@ -318,8 +318,7 @@
 	vt_list = &per_cpu(virt_cpu_timer, timer->cpu);
 	spin_lock_irqsave(&vt_list->lock, flags);
 
-	if (timer->cpu != smp_processor_id())
-		printk("internal_add_vtimer: BUG, running on wrong CPU");
+	BUG_ON(timer->cpu != smp_processor_id());
 
 	/* if list is empty we only have to set the timer */
 	if (list_empty(&vt_list->list)) {
@@ -353,25 +352,12 @@
 	put_cpu();
 }
 
-static inline int prepare_vtimer(struct vtimer_list *timer)
+static inline void prepare_vtimer(struct vtimer_list *timer)
 {
-	if (!timer->function) {
-		printk("add_virt_timer: uninitialized timer\n");
-		return -EINVAL;
-	}
-
-	if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) {
-		printk("add_virt_timer: invalid timer expire value!\n");
-		return -EINVAL;
-	}
-
-	if (vtimer_pending(timer)) {
-		printk("add_virt_timer: timer pending\n");
-		return -EBUSY;
-	}
-
+	BUG_ON(!timer->function);
+	BUG_ON(!timer->expires || timer->expires > VTIMER_MAX_SLICE);
+	BUG_ON(vtimer_pending(timer));
 	timer->cpu = get_cpu();
-	return 0;
 }
 
 /*
@@ -382,10 +368,7 @@
 	struct vtimer_list *timer;
 
 	timer = (struct vtimer_list *)new;
-
-	if (prepare_vtimer(timer) < 0)
-		return;
-
+	prepare_vtimer(timer);
 	timer->interval = 0;
 	internal_add_vtimer(timer);
 }
@@ -399,10 +382,7 @@
 	struct vtimer_list *timer;
 
 	timer = (struct vtimer_list *)new;
-
-	if (prepare_vtimer(timer) < 0)
-		return;
-
+	prepare_vtimer(timer);
 	timer->interval = timer->expires;
 	internal_add_vtimer(timer);
 }
@@ -423,15 +403,8 @@
 	unsigned long flags;
 	int cpu;
 
-	if (!timer->function) {
-		printk("mod_virt_timer: uninitialized timer\n");
-		return	-EINVAL;
-	}
-
-	if (!expires || expires > VTIMER_MAX_SLICE) {
-		printk("mod_virt_timer: invalid expire range\n");
-		return -EINVAL;
-	}
+	BUG_ON(!timer->function);
+	BUG_ON(!expires || expires > VTIMER_MAX_SLICE);
 
 	/*
 	 * This is a common optimization triggered by the
@@ -444,6 +417,9 @@
 	cpu = get_cpu();
 	vt_list = &per_cpu(virt_cpu_timer, cpu);
 
+	/* check if we run on the right CPU */
+	BUG_ON(timer->cpu != cpu);
+
 	/* disable interrupts before test if timer is pending */
 	spin_lock_irqsave(&vt_list->lock, flags);
 
@@ -458,14 +434,6 @@
 		return 0;
 	}
 
-	/* check if we run on the right CPU */
-	if (timer->cpu != cpu) {
-		printk("mod_virt_timer: running on wrong CPU, check your code\n");
-		spin_unlock_irqrestore(&vt_list->lock, flags);
-		put_cpu();
-		return -EINVAL;
-	}
-
 	list_del_init(&timer->entry);
 	timer->expires = expires;
 
@@ -536,24 +504,6 @@
 
 }
 
-static int vtimer_idle_notify(struct notifier_block *self,
-			      unsigned long action, void *hcpu)
-{
-	switch (action) {
-	case S390_CPU_IDLE:
-		stop_cpu_timer();
-		break;
-	case S390_CPU_NOT_IDLE:
-		start_cpu_timer();
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block vtimer_idle_nb = {
-	.notifier_call = vtimer_idle_notify,
-};
-
 void __init vtime_init(void)
 {
 	/* request the cpu timer external interrupt */
@@ -561,9 +511,6 @@
 					      &ext_int_info_timer) != 0)
 		panic("Couldn't request external interrupt 0x1005");
 
-	if (register_idle_notifier(&vtimer_idle_nb))
-		panic("Couldn't register idle notifier");
-
 	/* Enable cpu timer interrupts on the boot cpu. */
 	init_cpu_vtimer();
 }
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index 0559864..388cc74 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -202,3 +202,22 @@
         }
 }
 #endif
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+int arch_add_memory(int nid, u64 start, u64 size)
+{
+	struct pglist_data *pgdat;
+	struct zone *zone;
+	int rc;
+
+	pgdat = NODE_DATA(nid);
+	zone = pgdat->node_zones + ZONE_NORMAL;
+	rc = vmem_add_mapping(start, size);
+	if (rc)
+		return rc;
+	rc = __add_pages(zone, PFN_DOWN(start), PFN_DOWN(size));
+	if (rc)
+		vmem_remove_mapping(start, size);
+	return rc;
+}
+#endif /* CONFIG_MEMORY_HOTPLUG */
diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c
index 74e9929..2696a68 100644
--- a/arch/x86/kernel/traps_64.c
+++ b/arch/x86/kernel/traps_64.c
@@ -105,30 +105,7 @@
 
 void printk_address(unsigned long address, int reliable)
 {
-#ifdef CONFIG_KALLSYMS
-	unsigned long offset = 0, symsize;
-	const char *symname;
-	char *modname;
-	char *delim = ":";
-	char namebuf[KSYM_NAME_LEN];
-	char reliab[4] = "";
-
-	symname = kallsyms_lookup(address, &symsize, &offset,
-					&modname, namebuf);
-	if (!symname) {
-		printk(" [<%016lx>]\n", address);
-		return;
-	}
-	if (!reliable)
-		strcpy(reliab, "? ");
-
-	if (!modname)
-		modname = delim = "";
-	printk(" [<%016lx>] %s%s%s%s%s+0x%lx/0x%lx\n",
-		address, reliab, delim, modname, delim, symname, offset, symsize);
-#else
-	printk(" [<%016lx>]\n", address);
-#endif
+	printk(" [<%016lx>] %s%pS\n", address, reliable ? "": "? ", (void *) address);
 }
 
 static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
diff --git a/block/Kconfig b/block/Kconfig
index 3e97f2b..1ab7c15 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -81,6 +81,18 @@
 
 	  If unsure, say N.
 
+config BLK_DEV_INTEGRITY
+	bool "Block layer data integrity support"
+	---help---
+	Some storage devices allow extra information to be
+	stored/retrieved to help protect the data.  The block layer
+	data integrity option provides hooks which can be used by
+	filesystems to ensure better data integrity.
+
+	Say yes here if you have a storage device that provides the
+	T10/SCSI Data Integrity Field or the T13/ATA External Path
+	Protection.  If in doubt, say N.
+
 endif # BLOCK
 
 config BLOCK_COMPAT
diff --git a/block/Makefile b/block/Makefile
index 5a43c7d..208000b 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -4,7 +4,8 @@
 
 obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \
 			blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \
-			blk-exec.o blk-merge.o ioctl.o genhd.o scsi_ioctl.o
+			blk-exec.o blk-merge.o ioctl.o genhd.o scsi_ioctl.o \
+			cmd-filter.o
 
 obj-$(CONFIG_BLK_DEV_BSG)	+= bsg.o
 obj-$(CONFIG_IOSCHED_NOOP)	+= noop-iosched.o
@@ -14,3 +15,4 @@
 
 obj-$(CONFIG_BLK_DEV_IO_TRACE)	+= blktrace.o
 obj-$(CONFIG_BLOCK_COMPAT)	+= compat_ioctl.o
+obj-$(CONFIG_BLK_DEV_INTEGRITY)	+= blk-integrity.o
diff --git a/block/as-iosched.c b/block/as-iosched.c
index 743f33a..9735acb 100644
--- a/block/as-iosched.c
+++ b/block/as-iosched.c
@@ -151,6 +151,7 @@
 
 static DEFINE_PER_CPU(unsigned long, ioc_count);
 static struct completion *ioc_gone;
+static DEFINE_SPINLOCK(ioc_gone_lock);
 
 static void as_move_to_dispatch(struct as_data *ad, struct request *rq);
 static void as_antic_stop(struct as_data *ad);
@@ -164,8 +165,19 @@
 {
 	kfree(aic);
 	elv_ioc_count_dec(ioc_count);
-	if (ioc_gone && !elv_ioc_count_read(ioc_count))
-		complete(ioc_gone);
+	if (ioc_gone) {
+		/*
+		 * AS scheduler is exiting, grab exit lock and check
+		 * the pending io context count. If it hits zero,
+		 * complete ioc_gone and set it back to NULL.
+		 */
+		spin_lock(&ioc_gone_lock);
+		if (ioc_gone && !elv_ioc_count_read(ioc_count)) {
+			complete(ioc_gone);
+			ioc_gone = NULL;
+		}
+		spin_unlock(&ioc_gone_lock);
+	}
 }
 
 static void as_trim(struct io_context *ioc)
@@ -1493,7 +1505,7 @@
 	/* ioc_gone's update must be visible before reading ioc_count */
 	smp_wmb();
 	if (elv_ioc_count_read(ioc_count))
-		wait_for_completion(ioc_gone);
+		wait_for_completion(&all_gone);
 	synchronize_rcu();
 }
 
diff --git a/block/blk-core.c b/block/blk-core.c
index 1905aab..dbc7f42 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -143,6 +143,10 @@
 
 		bio->bi_size -= nbytes;
 		bio->bi_sector += (nbytes >> 9);
+
+		if (bio_integrity(bio))
+			bio_integrity_advance(bio, nbytes);
+
 		if (bio->bi_size == 0)
 			bio_endio(bio, error);
 	} else {
@@ -201,8 +205,7 @@
 	if (blk_queue_stopped(q))
 		return;
 
-	if (!test_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags)) {
-		__set_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags);
+	if (!queue_flag_test_and_set(QUEUE_FLAG_PLUGGED, q)) {
 		mod_timer(&q->unplug_timer, jiffies + q->unplug_delay);
 		blk_add_trace_generic(q, NULL, 0, BLK_TA_PLUG);
 	}
@@ -217,10 +220,9 @@
 {
 	WARN_ON(!irqs_disabled());
 
-	if (!test_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags))
+	if (!queue_flag_test_and_clear(QUEUE_FLAG_PLUGGED, q))
 		return 0;
 
-	queue_flag_clear(QUEUE_FLAG_PLUGGED, q);
 	del_timer(&q->unplug_timer);
 	return 1;
 }
@@ -324,8 +326,7 @@
 	 * one level of recursion is ok and is much faster than kicking
 	 * the unplug handling
 	 */
-	if (!test_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) {
-		queue_flag_set(QUEUE_FLAG_REENTER, q);
+	if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) {
 		q->request_fn(q);
 		queue_flag_clear(QUEUE_FLAG_REENTER, q);
 	} else {
@@ -390,8 +391,7 @@
 	 * handling reinvoke the handler shortly if we already got there.
 	 */
 	if (!elv_queue_empty(q)) {
-		if (!test_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) {
-			queue_flag_set(QUEUE_FLAG_REENTER, q);
+		if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) {
 			q->request_fn(q);
 			queue_flag_clear(QUEUE_FLAG_REENTER, q);
 		} else {
@@ -1381,6 +1381,9 @@
 		 */
 		blk_partition_remap(bio);
 
+		if (bio_integrity_enabled(bio) && bio_integrity_prep(bio))
+			goto end_io;
+
 		if (old_sector != -1)
 			blk_add_trace_remap(q, bio, old_dev, bio->bi_sector,
 					    old_sector);
diff --git a/block/blk-integrity.c b/block/blk-integrity.c
new file mode 100644
index 0000000..3f1a847
--- /dev/null
+++ b/block/blk-integrity.c
@@ -0,0 +1,381 @@
+/*
+ * blk-integrity.c - Block layer data integrity extensions
+ *
+ * Copyright (C) 2007, 2008 Oracle Corporation
+ * Written by: Martin K. Petersen <martin.petersen@oracle.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/mempool.h>
+#include <linux/bio.h>
+#include <linux/scatterlist.h>
+
+#include "blk.h"
+
+static struct kmem_cache *integrity_cachep;
+
+/**
+ * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements
+ * @rq:		request with integrity metadata attached
+ *
+ * Description: Returns the number of elements required in a
+ * scatterlist corresponding to the integrity metadata in a request.
+ */
+int blk_rq_count_integrity_sg(struct request *rq)
+{
+	struct bio_vec *iv, *ivprv;
+	struct req_iterator iter;
+	unsigned int segments;
+
+	ivprv = NULL;
+	segments = 0;
+
+	rq_for_each_integrity_segment(iv, rq, iter) {
+
+		if (!ivprv || !BIOVEC_PHYS_MERGEABLE(ivprv, iv))
+			segments++;
+
+		ivprv = iv;
+	}
+
+	return segments;
+}
+EXPORT_SYMBOL(blk_rq_count_integrity_sg);
+
+/**
+ * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist
+ * @rq:		request with integrity metadata attached
+ * @sglist:	target scatterlist
+ *
+ * Description: Map the integrity vectors in request into a
+ * scatterlist.  The scatterlist must be big enough to hold all
+ * elements.  I.e. sized using blk_rq_count_integrity_sg().
+ */
+int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist)
+{
+	struct bio_vec *iv, *ivprv;
+	struct req_iterator iter;
+	struct scatterlist *sg;
+	unsigned int segments;
+
+	ivprv = NULL;
+	sg = NULL;
+	segments = 0;
+
+	rq_for_each_integrity_segment(iv, rq, iter) {
+
+		if (ivprv) {
+			if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv))
+				goto new_segment;
+
+			sg->length += iv->bv_len;
+		} else {
+new_segment:
+			if (!sg)
+				sg = sglist;
+			else {
+				sg->page_link &= ~0x02;
+				sg = sg_next(sg);
+			}
+
+			sg_set_page(sg, iv->bv_page, iv->bv_len, iv->bv_offset);
+			segments++;
+		}
+
+		ivprv = iv;
+	}
+
+	if (sg)
+		sg_mark_end(sg);
+
+	return segments;
+}
+EXPORT_SYMBOL(blk_rq_map_integrity_sg);
+
+/**
+ * blk_integrity_compare - Compare integrity profile of two block devices
+ * @b1:		Device to compare
+ * @b2:		Device to compare
+ *
+ * Description: Meta-devices like DM and MD need to verify that all
+ * sub-devices use the same integrity format before advertising to
+ * upper layers that they can send/receive integrity metadata.  This
+ * function can be used to check whether two block devices have
+ * compatible integrity formats.
+ */
+int blk_integrity_compare(struct block_device *bd1, struct block_device *bd2)
+{
+	struct blk_integrity *b1 = bd1->bd_disk->integrity;
+	struct blk_integrity *b2 = bd2->bd_disk->integrity;
+
+	BUG_ON(bd1->bd_disk == NULL);
+	BUG_ON(bd2->bd_disk == NULL);
+
+	if (!b1 || !b2)
+		return 0;
+
+	if (b1->sector_size != b2->sector_size) {
+		printk(KERN_ERR "%s: %s/%s sector sz %u != %u\n", __func__,
+		       bd1->bd_disk->disk_name, bd2->bd_disk->disk_name,
+		       b1->sector_size, b2->sector_size);
+		return -1;
+	}
+
+	if (b1->tuple_size != b2->tuple_size) {
+		printk(KERN_ERR "%s: %s/%s tuple sz %u != %u\n", __func__,
+		       bd1->bd_disk->disk_name, bd2->bd_disk->disk_name,
+		       b1->tuple_size, b2->tuple_size);
+		return -1;
+	}
+
+	if (b1->tag_size && b2->tag_size && (b1->tag_size != b2->tag_size)) {
+		printk(KERN_ERR "%s: %s/%s tag sz %u != %u\n", __func__,
+		       bd1->bd_disk->disk_name, bd2->bd_disk->disk_name,
+		       b1->tag_size, b2->tag_size);
+		return -1;
+	}
+
+	if (strcmp(b1->name, b2->name)) {
+		printk(KERN_ERR "%s: %s/%s type %s != %s\n", __func__,
+		       bd1->bd_disk->disk_name, bd2->bd_disk->disk_name,
+		       b1->name, b2->name);
+		return -1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(blk_integrity_compare);
+
+struct integrity_sysfs_entry {
+	struct attribute attr;
+	ssize_t (*show)(struct blk_integrity *, char *);
+	ssize_t (*store)(struct blk_integrity *, const char *, size_t);
+};
+
+static ssize_t integrity_attr_show(struct kobject *kobj, struct attribute *attr,
+				   char *page)
+{
+	struct blk_integrity *bi =
+		container_of(kobj, struct blk_integrity, kobj);
+	struct integrity_sysfs_entry *entry =
+		container_of(attr, struct integrity_sysfs_entry, attr);
+
+	return entry->show(bi, page);
+}
+
+static ssize_t integrity_attr_store(struct kobject *kobj,
+				    struct attribute *attr, const char *page,
+				    size_t count)
+{
+	struct blk_integrity *bi =
+		container_of(kobj, struct blk_integrity, kobj);
+	struct integrity_sysfs_entry *entry =
+		container_of(attr, struct integrity_sysfs_entry, attr);
+	ssize_t ret = 0;
+
+	if (entry->store)
+		ret = entry->store(bi, page, count);
+
+	return ret;
+}
+
+static ssize_t integrity_format_show(struct blk_integrity *bi, char *page)
+{
+	if (bi != NULL && bi->name != NULL)
+		return sprintf(page, "%s\n", bi->name);
+	else
+		return sprintf(page, "none\n");
+}
+
+static ssize_t integrity_tag_size_show(struct blk_integrity *bi, char *page)
+{
+	if (bi != NULL)
+		return sprintf(page, "%u\n", bi->tag_size);
+	else
+		return sprintf(page, "0\n");
+}
+
+static ssize_t integrity_read_store(struct blk_integrity *bi,
+				    const char *page, size_t count)
+{
+	char *p = (char *) page;
+	unsigned long val = simple_strtoul(p, &p, 10);
+
+	if (val)
+		bi->flags |= INTEGRITY_FLAG_READ;
+	else
+		bi->flags &= ~INTEGRITY_FLAG_READ;
+
+	return count;
+}
+
+static ssize_t integrity_read_show(struct blk_integrity *bi, char *page)
+{
+	return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_READ) != 0);
+}
+
+static ssize_t integrity_write_store(struct blk_integrity *bi,
+				     const char *page, size_t count)
+{
+	char *p = (char *) page;
+	unsigned long val = simple_strtoul(p, &p, 10);
+
+	if (val)
+		bi->flags |= INTEGRITY_FLAG_WRITE;
+	else
+		bi->flags &= ~INTEGRITY_FLAG_WRITE;
+
+	return count;
+}
+
+static ssize_t integrity_write_show(struct blk_integrity *bi, char *page)
+{
+	return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_WRITE) != 0);
+}
+
+static struct integrity_sysfs_entry integrity_format_entry = {
+	.attr = { .name = "format", .mode = S_IRUGO },
+	.show = integrity_format_show,
+};
+
+static struct integrity_sysfs_entry integrity_tag_size_entry = {
+	.attr = { .name = "tag_size", .mode = S_IRUGO },
+	.show = integrity_tag_size_show,
+};
+
+static struct integrity_sysfs_entry integrity_read_entry = {
+	.attr = { .name = "read_verify", .mode = S_IRUGO | S_IWUSR },
+	.show = integrity_read_show,
+	.store = integrity_read_store,
+};
+
+static struct integrity_sysfs_entry integrity_write_entry = {
+	.attr = { .name = "write_generate", .mode = S_IRUGO | S_IWUSR },
+	.show = integrity_write_show,
+	.store = integrity_write_store,
+};
+
+static struct attribute *integrity_attrs[] = {
+	&integrity_format_entry.attr,
+	&integrity_tag_size_entry.attr,
+	&integrity_read_entry.attr,
+	&integrity_write_entry.attr,
+	NULL,
+};
+
+static struct sysfs_ops integrity_ops = {
+	.show	= &integrity_attr_show,
+	.store	= &integrity_attr_store,
+};
+
+static int __init blk_dev_integrity_init(void)
+{
+	integrity_cachep = kmem_cache_create("blkdev_integrity",
+					     sizeof(struct blk_integrity),
+					     0, SLAB_PANIC, NULL);
+	return 0;
+}
+subsys_initcall(blk_dev_integrity_init);
+
+static void blk_integrity_release(struct kobject *kobj)
+{
+	struct blk_integrity *bi =
+		container_of(kobj, struct blk_integrity, kobj);
+
+	kmem_cache_free(integrity_cachep, bi);
+}
+
+static struct kobj_type integrity_ktype = {
+	.default_attrs	= integrity_attrs,
+	.sysfs_ops	= &integrity_ops,
+	.release	= blk_integrity_release,
+};
+
+/**
+ * blk_integrity_register - Register a gendisk as being integrity-capable
+ * @disk:	struct gendisk pointer to make integrity-aware
+ * @template:	integrity profile
+ *
+ * Description: When a device needs to advertise itself as being able
+ * to send/receive integrity metadata it must use this function to
+ * register the capability with the block layer.  The template is a
+ * blk_integrity struct with values appropriate for the underlying
+ * hardware.  See Documentation/block/data-integrity.txt.
+ */
+int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template)
+{
+	struct blk_integrity *bi;
+
+	BUG_ON(disk == NULL);
+	BUG_ON(template == NULL);
+
+	if (disk->integrity == NULL) {
+		bi = kmem_cache_alloc(integrity_cachep,
+						GFP_KERNEL | __GFP_ZERO);
+		if (!bi)
+			return -1;
+
+		if (kobject_init_and_add(&bi->kobj, &integrity_ktype,
+					 &disk->dev.kobj, "%s", "integrity")) {
+			kmem_cache_free(integrity_cachep, bi);
+			return -1;
+		}
+
+		kobject_uevent(&bi->kobj, KOBJ_ADD);
+
+		bi->flags |= INTEGRITY_FLAG_READ | INTEGRITY_FLAG_WRITE;
+		bi->sector_size = disk->queue->hardsect_size;
+		disk->integrity = bi;
+	} else
+		bi = disk->integrity;
+
+	/* Use the provided profile as template */
+	bi->name = template->name;
+	bi->generate_fn = template->generate_fn;
+	bi->verify_fn = template->verify_fn;
+	bi->tuple_size = template->tuple_size;
+	bi->set_tag_fn = template->set_tag_fn;
+	bi->get_tag_fn = template->get_tag_fn;
+	bi->tag_size = template->tag_size;
+
+	return 0;
+}
+EXPORT_SYMBOL(blk_integrity_register);
+
+/**
+ * blk_integrity_unregister - Remove block integrity profile
+ * @disk:	disk whose integrity profile to deallocate
+ *
+ * Description: This function frees all memory used by the block
+ * integrity profile.  To be called at device teardown.
+ */
+void blk_integrity_unregister(struct gendisk *disk)
+{
+	struct blk_integrity *bi;
+
+	if (!disk || !disk->integrity)
+		return;
+
+	bi = disk->integrity;
+
+	kobject_uevent(&bi->kobj, KOBJ_REMOVE);
+	kobject_del(&bi->kobj);
+	kobject_put(&disk->dev.kobj);
+	kmem_cache_free(integrity_cachep, bi);
+}
+EXPORT_SYMBOL(blk_integrity_unregister);
diff --git a/block/blk-map.c b/block/blk-map.c
index 0b1af5a..ddd96fb 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -210,6 +210,7 @@
 	if (!bio_flagged(bio, BIO_USER_MAPPED))
 		rq->cmd_flags |= REQ_COPY_USER;
 
+	blk_queue_bounce(q, &bio);
 	bio_get(bio);
 	blk_rq_bio_prep(q, rq, bio);
 	rq->buffer = rq->data = NULL;
@@ -268,6 +269,7 @@
 	int reading = rq_data_dir(rq) == READ;
 	int do_copy = 0;
 	struct bio *bio;
+	unsigned long stack_mask = ~(THREAD_SIZE - 1);
 
 	if (len > (q->max_hw_sectors << 9))
 		return -EINVAL;
@@ -278,6 +280,10 @@
 	alignment = queue_dma_alignment(q) | q->dma_pad_mask;
 	do_copy = ((kaddr & alignment) || (len & alignment));
 
+	if (!((kaddr & stack_mask) ^
+	      ((unsigned long)current->stack & stack_mask)))
+		do_copy = 1;
+
 	if (do_copy)
 		bio = bio_copy_kern(q, kbuf, len, gfp_mask, reading);
 	else
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 651136a..5efc9e7 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -441,6 +441,9 @@
 	    || next->special)
 		return 0;
 
+	if (blk_integrity_rq(req) != blk_integrity_rq(next))
+		return 0;
+
 	/*
 	 * If we are allowed to merge, then append bio list
 	 * from next to rq and release next. merge_requests_fn
diff --git a/block/blk-settings.c b/block/blk-settings.c
index 8dd8641..dfc7701 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -302,11 +302,10 @@
  * @q:     the request queue for the device
  * @mask:  pad mask
  *
- * Set pad mask.  Direct IO requests are padded to the mask specified.
+ * Set dma pad mask.
  *
- * Appending pad buffer to a request modifies ->data_len such that it
- * includes the pad buffer.  The original requested data length can be
- * obtained using blk_rq_raw_data_len().
+ * Appending pad buffer to a request modifies the last entry of a
+ * scatter list such that it includes the pad buffer.
  **/
 void blk_queue_dma_pad(struct request_queue *q, unsigned int mask)
 {
@@ -315,6 +314,23 @@
 EXPORT_SYMBOL(blk_queue_dma_pad);
 
 /**
+ * blk_queue_update_dma_pad - update pad mask
+ * @q:     the request queue for the device
+ * @mask:  pad mask
+ *
+ * Update dma pad mask.
+ *
+ * Appending pad buffer to a request modifies the last entry of a
+ * scatter list such that it includes the pad buffer.
+ **/
+void blk_queue_update_dma_pad(struct request_queue *q, unsigned int mask)
+{
+	if (mask > q->dma_pad_mask)
+		q->dma_pad_mask = mask;
+}
+EXPORT_SYMBOL(blk_queue_update_dma_pad);
+
+/**
  * blk_queue_dma_drain - Set up a drain buffer for excess dma.
  * @q:  the request queue for the device
  * @dma_drain_needed: fn which returns non-zero if drain is necessary
diff --git a/block/blk.h b/block/blk.h
index 59776ab..c79f30e 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -51,4 +51,12 @@
 	return q->nr_congestion_off;
 }
 
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+
+#define rq_for_each_integrity_segment(bvl, _rq, _iter)		\
+	__rq_for_each_bio(_iter.bio, _rq)			\
+		bip_for_each_vec(bvl, _iter.bio->bi_integrity, _iter.i)
+
+#endif /* BLK_DEV_INTEGRITY */
+
 #endif
diff --git a/block/blktrace.c b/block/blktrace.c
index 8d3a277..eb9651c 100644
--- a/block/blktrace.c
+++ b/block/blktrace.c
@@ -244,6 +244,7 @@
 static void blk_trace_cleanup(struct blk_trace *bt)
 {
 	relay_close(bt->rchan);
+	debugfs_remove(bt->msg_file);
 	debugfs_remove(bt->dropped_file);
 	blk_remove_tree(bt->dir);
 	free_percpu(bt->sequence);
@@ -291,6 +292,44 @@
 	.read =		blk_dropped_read,
 };
 
+static int blk_msg_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+
+	return 0;
+}
+
+static ssize_t blk_msg_write(struct file *filp, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	char *msg;
+	struct blk_trace *bt;
+
+	if (count > BLK_TN_MAX_MSG)
+		return -EINVAL;
+
+	msg = kmalloc(count, GFP_KERNEL);
+	if (msg == NULL)
+		return -ENOMEM;
+
+	if (copy_from_user(msg, buffer, count)) {
+		kfree(msg);
+		return -EFAULT;
+	}
+
+	bt = filp->private_data;
+	__trace_note_message(bt, "%s", msg);
+	kfree(msg);
+
+	return count;
+}
+
+static const struct file_operations blk_msg_fops = {
+	.owner =	THIS_MODULE,
+	.open =		blk_msg_open,
+	.write =	blk_msg_write,
+};
+
 /*
  * Keep track of how many times we encountered a full subbuffer, to aid
  * the user space app in telling how many lost events there were.
@@ -380,6 +419,10 @@
 	if (!bt->dropped_file)
 		goto err;
 
+	bt->msg_file = debugfs_create_file("msg", 0222, dir, bt, &blk_msg_fops);
+	if (!bt->msg_file)
+		goto err;
+
 	bt->rchan = relay_open("trace", dir, buts->buf_size,
 				buts->buf_nr, &blk_relay_callbacks, bt);
 	if (!bt->rchan)
@@ -409,6 +452,8 @@
 	if (dir)
 		blk_remove_tree(dir);
 	if (bt) {
+		if (bt->msg_file)
+			debugfs_remove(bt->msg_file);
 		if (bt->dropped_file)
 			debugfs_remove(bt->dropped_file);
 		free_percpu(bt->sequence);
diff --git a/block/bsg.c b/block/bsg.c
index 54d617f..93e757d 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -44,11 +44,12 @@
 	char name[BUS_ID_SIZE];
 	int max_queue;
 	unsigned long flags;
+	struct blk_scsi_cmd_filter *cmd_filter;
+	mode_t *f_mode;
 };
 
 enum {
 	BSG_F_BLOCK		= 1,
-	BSG_F_WRITE_PERM	= 2,
 };
 
 #define BSG_DEFAULT_CMDS	64
@@ -172,7 +173,7 @@
 }
 
 static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq,
-				struct sg_io_v4 *hdr, int has_write_perm)
+				struct sg_io_v4 *hdr, struct bsg_device *bd)
 {
 	if (hdr->request_len > BLK_MAX_CDB) {
 		rq->cmd = kzalloc(hdr->request_len, GFP_KERNEL);
@@ -185,7 +186,8 @@
 		return -EFAULT;
 
 	if (hdr->subprotocol == BSG_SUB_PROTOCOL_SCSI_CMD) {
-		if (blk_verify_command(rq->cmd, has_write_perm))
+		if (blk_cmd_filter_verify_command(bd->cmd_filter, rq->cmd,
+						 bd->f_mode))
 			return -EPERM;
 	} else if (!capable(CAP_SYS_RAWIO))
 		return -EPERM;
@@ -263,8 +265,7 @@
 	rq = blk_get_request(q, rw, GFP_KERNEL);
 	if (!rq)
 		return ERR_PTR(-ENOMEM);
-	ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM,
-						       &bd->flags));
+	ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, bd);
 	if (ret)
 		goto out;
 
@@ -566,12 +567,23 @@
 		set_bit(BSG_F_BLOCK, &bd->flags);
 }
 
-static inline void bsg_set_write_perm(struct bsg_device *bd, struct file *file)
+static void bsg_set_cmd_filter(struct bsg_device *bd,
+			   struct file *file)
 {
-	if (file->f_mode & FMODE_WRITE)
-		set_bit(BSG_F_WRITE_PERM, &bd->flags);
-	else
-		clear_bit(BSG_F_WRITE_PERM, &bd->flags);
+	struct inode *inode;
+	struct gendisk *disk;
+
+	if (!file)
+		return;
+
+	inode = file->f_dentry->d_inode;
+	if (!inode)
+		return;
+
+	disk = inode->i_bdev->bd_disk;
+
+	bd->cmd_filter = &disk->cmd_filter;
+	bd->f_mode = &file->f_mode;
 }
 
 /*
@@ -595,6 +607,8 @@
 	dprintk("%s: read %Zd bytes\n", bd->name, count);
 
 	bsg_set_block(bd, file);
+	bsg_set_cmd_filter(bd, file);
+
 	bytes_read = 0;
 	ret = __bsg_read(buf, count, bd, NULL, &bytes_read);
 	*ppos = bytes_read;
@@ -668,7 +682,7 @@
 	dprintk("%s: write %Zd bytes\n", bd->name, count);
 
 	bsg_set_block(bd, file);
-	bsg_set_write_perm(bd, file);
+	bsg_set_cmd_filter(bd, file);
 
 	bytes_written = 0;
 	ret = __bsg_write(bd, buf, count, &bytes_written);
@@ -772,7 +786,9 @@
 	}
 
 	bd->queue = rq;
+
 	bsg_set_block(bd, file);
+	bsg_set_cmd_filter(bd, file);
 
 	atomic_set(&bd->ref_count, 1);
 	mutex_lock(&bsg_mutex);
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index d01b411..1e2aff8 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -11,6 +11,7 @@
 #include <linux/elevator.h>
 #include <linux/rbtree.h>
 #include <linux/ioprio.h>
+#include <linux/blktrace_api.h>
 
 /*
  * tunables
@@ -41,13 +42,14 @@
 
 #define RQ_CIC(rq)		\
 	((struct cfq_io_context *) (rq)->elevator_private)
-#define RQ_CFQQ(rq)		((rq)->elevator_private2)
+#define RQ_CFQQ(rq)		(struct cfq_queue *) ((rq)->elevator_private2)
 
 static struct kmem_cache *cfq_pool;
 static struct kmem_cache *cfq_ioc_pool;
 
 static DEFINE_PER_CPU(unsigned long, ioc_count);
 static struct completion *ioc_gone;
+static DEFINE_SPINLOCK(ioc_gone_lock);
 
 #define CFQ_PRIO_LISTS		IOPRIO_BE_NR
 #define cfq_class_idle(cfqq)	((cfqq)->ioprio_class == IOPRIO_CLASS_IDLE)
@@ -155,6 +157,7 @@
 	unsigned short ioprio, org_ioprio;
 	unsigned short ioprio_class, org_ioprio_class;
 
+	pid_t pid;
 };
 
 enum cfqq_state_flags {
@@ -198,6 +201,11 @@
 CFQ_CFQQ_FNS(sync);
 #undef CFQ_CFQQ_FNS
 
+#define cfq_log_cfqq(cfqd, cfqq, fmt, args...)	\
+	blk_add_trace_msg((cfqd)->queue, "cfq%d " fmt, (cfqq)->pid, ##args)
+#define cfq_log(cfqd, fmt, args...)	\
+	blk_add_trace_msg((cfqd)->queue, "cfq " fmt, ##args)
+
 static void cfq_dispatch_insert(struct request_queue *, struct request *);
 static struct cfq_queue *cfq_get_queue(struct cfq_data *, int,
 				       struct io_context *, gfp_t);
@@ -234,8 +242,10 @@
  */
 static inline void cfq_schedule_dispatch(struct cfq_data *cfqd)
 {
-	if (cfqd->busy_queues)
+	if (cfqd->busy_queues) {
+		cfq_log(cfqd, "schedule dispatch");
 		kblockd_schedule_work(&cfqd->unplug_work);
+	}
 }
 
 static int cfq_queue_empty(struct request_queue *q)
@@ -270,6 +280,7 @@
 cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
 	cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies;
+	cfq_log_cfqq(cfqd, cfqq, "set_slice=%lu", cfqq->slice_end - jiffies);
 }
 
 /*
@@ -539,6 +550,7 @@
  */
 static void cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
+	cfq_log_cfqq(cfqd, cfqq, "add_to_rr");
 	BUG_ON(cfq_cfqq_on_rr(cfqq));
 	cfq_mark_cfqq_on_rr(cfqq);
 	cfqd->busy_queues++;
@@ -552,6 +564,7 @@
  */
 static void cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
+	cfq_log_cfqq(cfqd, cfqq, "del_from_rr");
 	BUG_ON(!cfq_cfqq_on_rr(cfqq));
 	cfq_clear_cfqq_on_rr(cfqq);
 
@@ -638,6 +651,8 @@
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 
 	cfqd->rq_in_driver++;
+	cfq_log_cfqq(cfqd, RQ_CFQQ(rq), "activate rq, drv=%d",
+						cfqd->rq_in_driver);
 
 	/*
 	 * If the depth is larger 1, it really could be queueing. But lets
@@ -657,6 +672,8 @@
 
 	WARN_ON(!cfqd->rq_in_driver);
 	cfqd->rq_in_driver--;
+	cfq_log_cfqq(cfqd, RQ_CFQQ(rq), "deactivate rq, drv=%d",
+						cfqd->rq_in_driver);
 }
 
 static void cfq_remove_request(struct request *rq)
@@ -746,6 +763,7 @@
 				   struct cfq_queue *cfqq)
 {
 	if (cfqq) {
+		cfq_log_cfqq(cfqd, cfqq, "set_active");
 		cfqq->slice_end = 0;
 		cfq_clear_cfqq_must_alloc_slice(cfqq);
 		cfq_clear_cfqq_fifo_expire(cfqq);
@@ -763,6 +781,8 @@
 __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq,
 		    int timed_out)
 {
+	cfq_log_cfqq(cfqd, cfqq, "slice expired t=%d", timed_out);
+
 	if (cfq_cfqq_wait_request(cfqq))
 		del_timer(&cfqd->idle_slice_timer);
 
@@ -772,8 +792,10 @@
 	/*
 	 * store what was left of this slice, if the queue idled/timed out
 	 */
-	if (timed_out && !cfq_cfqq_slice_new(cfqq))
+	if (timed_out && !cfq_cfqq_slice_new(cfqq)) {
 		cfqq->slice_resid = cfqq->slice_end - jiffies;
+		cfq_log_cfqq(cfqd, cfqq, "resid=%ld", cfqq->slice_resid);
+	}
 
 	cfq_resort_rr_list(cfqd, cfqq);
 
@@ -866,6 +888,12 @@
 		return;
 
 	/*
+	 * still requests with the driver, don't idle
+	 */
+	if (cfqd->rq_in_driver)
+		return;
+
+	/*
 	 * task has exited, don't wait
 	 */
 	cic = cfqd->active_cic;
@@ -892,6 +920,7 @@
 		sl = min(sl, msecs_to_jiffies(CFQ_MIN_TT));
 
 	mod_timer(&cfqd->idle_slice_timer, jiffies + sl);
+	cfq_log(cfqd, "arm_idle: %lu", sl);
 }
 
 /*
@@ -902,6 +931,8 @@
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct cfq_queue *cfqq = RQ_CFQQ(rq);
 
+	cfq_log_cfqq(cfqd, cfqq, "dispatch_insert");
+
 	cfq_remove_request(rq);
 	cfqq->dispatched++;
 	elv_dispatch_sort(q, rq);
@@ -931,8 +962,9 @@
 	rq = rq_entry_fifo(cfqq->fifo.next);
 
 	if (time_before(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo]))
-		return NULL;
+		rq = NULL;
 
+	cfq_log_cfqq(cfqd, cfqq, "fifo=%p", rq);
 	return rq;
 }
 
@@ -1072,6 +1104,7 @@
 
 	BUG_ON(cfqd->busy_queues);
 
+	cfq_log(cfqd, "forced_dispatch=%d\n", dispatched);
 	return dispatched;
 }
 
@@ -1112,6 +1145,7 @@
 		dispatched += __cfq_dispatch_requests(cfqd, cfqq, max_dispatch);
 	}
 
+	cfq_log(cfqd, "dispatched=%d", dispatched);
 	return dispatched;
 }
 
@@ -1130,6 +1164,7 @@
 	if (!atomic_dec_and_test(&cfqq->ref))
 		return;
 
+	cfq_log_cfqq(cfqd, cfqq, "put_queue");
 	BUG_ON(rb_first(&cfqq->sort_list));
 	BUG_ON(cfqq->allocated[READ] + cfqq->allocated[WRITE]);
 	BUG_ON(cfq_cfqq_on_rr(cfqq));
@@ -1177,8 +1212,19 @@
 	kmem_cache_free(cfq_ioc_pool, cic);
 	elv_ioc_count_dec(ioc_count);
 
-	if (ioc_gone && !elv_ioc_count_read(ioc_count))
-		complete(ioc_gone);
+	if (ioc_gone) {
+		/*
+		 * CFQ scheduler is exiting, grab exit lock and check
+		 * the pending io context count. If it hits zero,
+		 * complete ioc_gone and set it back to NULL
+		 */
+		spin_lock(&ioc_gone_lock);
+		if (ioc_gone && !elv_ioc_count_read(ioc_count)) {
+			complete(ioc_gone);
+			ioc_gone = NULL;
+		}
+		spin_unlock(&ioc_gone_lock);
+	}
 }
 
 static void cfq_cic_free(struct cfq_io_context *cic)
@@ -1427,6 +1473,8 @@
 				cfq_mark_cfqq_idle_window(cfqq);
 			cfq_mark_cfqq_sync(cfqq);
 		}
+		cfqq->pid = current->pid;
+		cfq_log_cfqq(cfqd, cfqq, "alloced");
 	}
 
 	if (new_cfqq)
@@ -1675,7 +1723,7 @@
 cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq,
 		       struct cfq_io_context *cic)
 {
-	int enable_idle;
+	int old_idle, enable_idle;
 
 	/*
 	 * Don't idle for async or idle io prio class
@@ -1683,7 +1731,7 @@
 	if (!cfq_cfqq_sync(cfqq) || cfq_class_idle(cfqq))
 		return;
 
-	enable_idle = cfq_cfqq_idle_window(cfqq);
+	enable_idle = old_idle = cfq_cfqq_idle_window(cfqq);
 
 	if (!atomic_read(&cic->ioc->nr_tasks) || !cfqd->cfq_slice_idle ||
 	    (cfqd->hw_tag && CIC_SEEKY(cic)))
@@ -1695,10 +1743,13 @@
 			enable_idle = 1;
 	}
 
-	if (enable_idle)
-		cfq_mark_cfqq_idle_window(cfqq);
-	else
-		cfq_clear_cfqq_idle_window(cfqq);
+	if (old_idle != enable_idle) {
+		cfq_log_cfqq(cfqd, cfqq, "idle=%d", enable_idle);
+		if (enable_idle)
+			cfq_mark_cfqq_idle_window(cfqq);
+		else
+			cfq_clear_cfqq_idle_window(cfqq);
+	}
 }
 
 /*
@@ -1757,6 +1808,7 @@
  */
 static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq)
 {
+	cfq_log_cfqq(cfqd, cfqq, "preempt");
 	cfq_slice_expired(cfqd, 1);
 
 	/*
@@ -1818,6 +1870,7 @@
 	struct cfq_data *cfqd = q->elevator->elevator_data;
 	struct cfq_queue *cfqq = RQ_CFQQ(rq);
 
+	cfq_log_cfqq(cfqd, cfqq, "insert_request");
 	cfq_init_prio_data(cfqq, RQ_CIC(rq)->ioc);
 
 	cfq_add_rq_rb(rq);
@@ -1835,6 +1888,7 @@
 	unsigned long now;
 
 	now = jiffies;
+	cfq_log_cfqq(cfqd, cfqq, "complete");
 
 	WARN_ON(!cfqd->rq_in_driver);
 	WARN_ON(!cfqq->dispatched);
@@ -2004,6 +2058,7 @@
 
 	cfq_schedule_dispatch(cfqd);
 	spin_unlock_irqrestore(q->queue_lock, flags);
+	cfq_log(cfqd, "set_request fail");
 	return 1;
 }
 
@@ -2029,6 +2084,8 @@
 	unsigned long flags;
 	int timed_out = 1;
 
+	cfq_log(cfqd, "idle timer fired");
+
 	spin_lock_irqsave(cfqd->queue->queue_lock, flags);
 
 	cfqq = cfqd->active_queue;
@@ -2317,7 +2374,7 @@
 	 * pending RCU callbacks
 	 */
 	if (elv_ioc_count_read(ioc_count))
-		wait_for_completion(ioc_gone);
+		wait_for_completion(&all_gone);
 	cfq_slab_kill();
 }
 
diff --git a/block/cmd-filter.c b/block/cmd-filter.c
new file mode 100644
index 0000000..eec4404
--- /dev/null
+++ b/block/cmd-filter.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2004 Peter M. Jones <pjones@redhat.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public Licens
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/spinlock.h>
+#include <linux/parser.h>
+#include <linux/capability.h>
+#include <linux/bitops.h>
+
+#include <scsi/scsi.h>
+#include <linux/cdrom.h>
+
+int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter,
+				  unsigned char *cmd, mode_t *f_mode)
+{
+	/* root can do any command. */
+	if (capable(CAP_SYS_RAWIO))
+		return 0;
+
+	/* if there's no filter set, assume we're filtering everything out */
+	if (!filter)
+		return -EPERM;
+
+	/* Anybody who can open the device can do a read-safe command */
+	if (test_bit(cmd[0], filter->read_ok))
+		return 0;
+
+	/* Write-safe commands require a writable open */
+	if (test_bit(cmd[0], filter->write_ok) && (*f_mode & FMODE_WRITE))
+		return 0;
+
+	return -EPERM;
+}
+EXPORT_SYMBOL(blk_cmd_filter_verify_command);
+
+int blk_verify_command(struct file *file, unsigned char *cmd)
+{
+	struct gendisk *disk;
+	struct inode *inode;
+
+	if (!file)
+		return -EINVAL;
+
+	inode = file->f_dentry->d_inode;
+	if (!inode)
+		return -EINVAL;
+
+	disk = inode->i_bdev->bd_disk;
+
+	return blk_cmd_filter_verify_command(&disk->cmd_filter,
+						 cmd, &file->f_mode);
+}
+EXPORT_SYMBOL(blk_verify_command);
+
+/* and now, the sysfs stuff */
+static ssize_t rcf_cmds_show(struct blk_scsi_cmd_filter *filter, char *page,
+			     int rw)
+{
+	char *npage = page;
+	unsigned long *okbits;
+	int i;
+
+	if (rw == READ)
+		okbits = filter->read_ok;
+	else
+		okbits = filter->write_ok;
+
+	for (i = 0; i < BLK_SCSI_MAX_CMDS; i++) {
+		if (test_bit(i, okbits)) {
+			sprintf(npage, "%02x", i);
+			npage += 2;
+			if (i < BLK_SCSI_MAX_CMDS - 1)
+				sprintf(npage++, " ");
+		}
+	}
+
+	if (npage != page)
+		npage += sprintf(npage, "\n");
+
+	return npage - page;
+}
+
+static ssize_t rcf_readcmds_show(struct blk_scsi_cmd_filter *filter, char *page)
+{
+	return rcf_cmds_show(filter, page, READ);
+}
+
+static ssize_t rcf_writecmds_show(struct blk_scsi_cmd_filter *filter,
+				 char *page)
+{
+	return rcf_cmds_show(filter, page, WRITE);
+}
+
+static ssize_t rcf_cmds_store(struct blk_scsi_cmd_filter *filter,
+			      const char *page, size_t count, int rw)
+{
+	ssize_t ret = 0;
+	unsigned long okbits[BLK_SCSI_CMD_PER_LONG], *target_okbits;
+	int cmd, status, len;
+	substring_t ss;
+
+	memset(&okbits, 0, sizeof(okbits));
+
+	for (len = strlen(page); len > 0; len -= 3) {
+		if (len < 2)
+			break;
+		ss.from = (char *) page + ret;
+		ss.to = (char *) page + ret + 2;
+		ret += 3;
+		status = match_hex(&ss, &cmd);
+		/* either of these cases means invalid input, so do nothing. */
+		if (status || cmd >= BLK_SCSI_MAX_CMDS)
+			return -EINVAL;
+
+		__set_bit(cmd, okbits);
+	}
+
+	if (rw == READ)
+		target_okbits = filter->read_ok;
+	else
+		target_okbits = filter->write_ok;
+
+	memmove(target_okbits, okbits, sizeof(okbits));
+	return count;
+}
+
+static ssize_t rcf_readcmds_store(struct blk_scsi_cmd_filter *filter,
+				  const char *page, size_t count)
+{
+	return rcf_cmds_store(filter, page, count, READ);
+}
+
+static ssize_t rcf_writecmds_store(struct blk_scsi_cmd_filter *filter,
+				   const char *page, size_t count)
+{
+	return rcf_cmds_store(filter, page, count, WRITE);
+}
+
+struct rcf_sysfs_entry {
+	struct attribute attr;
+	ssize_t (*show)(struct blk_scsi_cmd_filter *, char *);
+	ssize_t (*store)(struct blk_scsi_cmd_filter *, const char *, size_t);
+};
+
+static struct rcf_sysfs_entry rcf_readcmds_entry = {
+	.attr = { .name = "read_table", .mode = S_IRUGO | S_IWUSR },
+	.show = rcf_readcmds_show,
+	.store = rcf_readcmds_store,
+};
+
+static struct rcf_sysfs_entry rcf_writecmds_entry = {
+	.attr = {.name = "write_table", .mode = S_IRUGO | S_IWUSR },
+	.show = rcf_writecmds_show,
+	.store = rcf_writecmds_store,
+};
+
+static struct attribute *default_attrs[] = {
+	&rcf_readcmds_entry.attr,
+	&rcf_writecmds_entry.attr,
+	NULL,
+};
+
+#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr)
+
+static ssize_t
+rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+	struct rcf_sysfs_entry *entry = to_rcf(attr);
+	struct blk_scsi_cmd_filter *filter;
+
+	filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj);
+	if (entry->show)
+		return entry->show(filter, page);
+
+	return 0;
+}
+
+static ssize_t
+rcf_attr_store(struct kobject *kobj, struct attribute *attr,
+			const char *page, size_t length)
+{
+	struct rcf_sysfs_entry *entry = to_rcf(attr);
+	struct blk_scsi_cmd_filter *filter;
+
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	if (!entry->store)
+		return -EINVAL;
+
+	filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj);
+	return entry->store(filter, page, length);
+}
+
+static struct sysfs_ops rcf_sysfs_ops = {
+	.show = rcf_attr_show,
+	.store = rcf_attr_store,
+};
+
+static struct kobj_type rcf_ktype = {
+	.sysfs_ops = &rcf_sysfs_ops,
+	.default_attrs = default_attrs,
+};
+
+#ifndef MAINTENANCE_IN_CMD
+#define MAINTENANCE_IN_CMD 0xa3
+#endif
+
+static void rcf_set_defaults(struct blk_scsi_cmd_filter *filter)
+{
+	/* Basic read-only commands */
+	__set_bit(TEST_UNIT_READY, filter->read_ok);
+	__set_bit(REQUEST_SENSE, filter->read_ok);
+	__set_bit(READ_6, filter->read_ok);
+	__set_bit(READ_10, filter->read_ok);
+	__set_bit(READ_12, filter->read_ok);
+	__set_bit(READ_16, filter->read_ok);
+	__set_bit(READ_BUFFER, filter->read_ok);
+	__set_bit(READ_DEFECT_DATA, filter->read_ok);
+	__set_bit(READ_CAPACITY, filter->read_ok);
+	__set_bit(READ_LONG, filter->read_ok);
+	__set_bit(INQUIRY, filter->read_ok);
+	__set_bit(MODE_SENSE, filter->read_ok);
+	__set_bit(MODE_SENSE_10, filter->read_ok);
+	__set_bit(LOG_SENSE, filter->read_ok);
+	__set_bit(START_STOP, filter->read_ok);
+	__set_bit(GPCMD_VERIFY_10, filter->read_ok);
+	__set_bit(VERIFY_16, filter->read_ok);
+	__set_bit(REPORT_LUNS, filter->read_ok);
+	__set_bit(SERVICE_ACTION_IN, filter->read_ok);
+	__set_bit(RECEIVE_DIAGNOSTIC, filter->read_ok);
+	__set_bit(MAINTENANCE_IN_CMD, filter->read_ok);
+	__set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok);
+
+	/* Audio CD commands */
+	__set_bit(GPCMD_PLAY_CD, filter->read_ok);
+	__set_bit(GPCMD_PLAY_AUDIO_10, filter->read_ok);
+	__set_bit(GPCMD_PLAY_AUDIO_MSF, filter->read_ok);
+	__set_bit(GPCMD_PLAY_AUDIO_TI, filter->read_ok);
+	__set_bit(GPCMD_PAUSE_RESUME, filter->read_ok);
+
+	/* CD/DVD data reading */
+	__set_bit(GPCMD_READ_CD, filter->read_ok);
+	__set_bit(GPCMD_READ_CD_MSF, filter->read_ok);
+	__set_bit(GPCMD_READ_DISC_INFO, filter->read_ok);
+	__set_bit(GPCMD_READ_CDVD_CAPACITY, filter->read_ok);
+	__set_bit(GPCMD_READ_DVD_STRUCTURE, filter->read_ok);
+	__set_bit(GPCMD_READ_HEADER, filter->read_ok);
+	__set_bit(GPCMD_READ_TRACK_RZONE_INFO, filter->read_ok);
+	__set_bit(GPCMD_READ_SUBCHANNEL, filter->read_ok);
+	__set_bit(GPCMD_READ_TOC_PMA_ATIP, filter->read_ok);
+	__set_bit(GPCMD_REPORT_KEY, filter->read_ok);
+	__set_bit(GPCMD_SCAN, filter->read_ok);
+	__set_bit(GPCMD_GET_CONFIGURATION, filter->read_ok);
+	__set_bit(GPCMD_READ_FORMAT_CAPACITIES, filter->read_ok);
+	__set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, filter->read_ok);
+	__set_bit(GPCMD_GET_PERFORMANCE, filter->read_ok);
+	__set_bit(GPCMD_SEEK, filter->read_ok);
+	__set_bit(GPCMD_STOP_PLAY_SCAN, filter->read_ok);
+
+	/* Basic writing commands */
+	__set_bit(WRITE_6, filter->write_ok);
+	__set_bit(WRITE_10, filter->write_ok);
+	__set_bit(WRITE_VERIFY, filter->write_ok);
+	__set_bit(WRITE_12, filter->write_ok);
+	__set_bit(WRITE_VERIFY_12, filter->write_ok);
+	__set_bit(WRITE_16, filter->write_ok);
+	__set_bit(WRITE_LONG, filter->write_ok);
+	__set_bit(WRITE_LONG_2, filter->write_ok);
+	__set_bit(ERASE, filter->write_ok);
+	__set_bit(GPCMD_MODE_SELECT_10, filter->write_ok);
+	__set_bit(MODE_SELECT, filter->write_ok);
+	__set_bit(LOG_SELECT, filter->write_ok);
+	__set_bit(GPCMD_BLANK, filter->write_ok);
+	__set_bit(GPCMD_CLOSE_TRACK, filter->write_ok);
+	__set_bit(GPCMD_FLUSH_CACHE, filter->write_ok);
+	__set_bit(GPCMD_FORMAT_UNIT, filter->write_ok);
+	__set_bit(GPCMD_REPAIR_RZONE_TRACK, filter->write_ok);
+	__set_bit(GPCMD_RESERVE_RZONE_TRACK, filter->write_ok);
+	__set_bit(GPCMD_SEND_DVD_STRUCTURE, filter->write_ok);
+	__set_bit(GPCMD_SEND_EVENT, filter->write_ok);
+	__set_bit(GPCMD_SEND_KEY, filter->write_ok);
+	__set_bit(GPCMD_SEND_OPC, filter->write_ok);
+	__set_bit(GPCMD_SEND_CUE_SHEET, filter->write_ok);
+	__set_bit(GPCMD_SET_SPEED, filter->write_ok);
+	__set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, filter->write_ok);
+	__set_bit(GPCMD_LOAD_UNLOAD, filter->write_ok);
+	__set_bit(GPCMD_SET_STREAMING, filter->write_ok);
+}
+
+int blk_register_filter(struct gendisk *disk)
+{
+	int ret;
+	struct blk_scsi_cmd_filter *filter = &disk->cmd_filter;
+	struct kobject *parent = kobject_get(disk->holder_dir->parent);
+
+	if (!parent)
+		return -ENODEV;
+
+	ret = kobject_init_and_add(&filter->kobj, &rcf_ktype, parent,
+				 "%s", "cmd_filter");
+
+	if (ret < 0)
+		return ret;
+
+	rcf_set_defaults(filter);
+	return 0;
+}
+
+void blk_unregister_filter(struct gendisk *disk)
+{
+	struct blk_scsi_cmd_filter *filter = &disk->cmd_filter;
+
+	kobject_put(&filter->kobj);
+	kobject_put(disk->holder_dir->parent);
+}
+
diff --git a/block/elevator.c b/block/elevator.c
index 902dd13..ed6f8f3 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -86,6 +86,12 @@
 	if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special)
 		return 0;
 
+	/*
+	 * only merge integrity protected bio into ditto rq
+	 */
+	if (bio_integrity(bio) != blk_integrity_rq(rq))
+		return 0;
+
 	if (!elv_iosched_allow_merge(rq, bio))
 		return 0;
 
@@ -144,7 +150,7 @@
 		else
 			sprintf(elv, "%s-iosched", name);
 
-		request_module(elv);
+		request_module("%s", elv);
 		spin_lock(&elv_list_lock);
 		e = elevator_find(name);
 	}
diff --git a/block/genhd.c b/block/genhd.c
index b922d48..9074f38 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -189,6 +189,7 @@
 			    disk->minors, NULL, exact_match, exact_lock, disk);
 	register_disk(disk);
 	blk_register_queue(disk);
+	blk_register_filter(disk);
 
 	bdi = &disk->queue->backing_dev_info;
 	bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor));
@@ -200,6 +201,7 @@
 
 void unlink_gendisk(struct gendisk *disk)
 {
+	blk_unregister_filter(disk);
 	sysfs_remove_link(&disk->dev.kobj, "bdi");
 	bdi_unregister(&disk->queue->backing_dev_info);
 	blk_unregister_queue(disk);
@@ -400,6 +402,14 @@
 		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
 }
 
+static ssize_t disk_ro_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct gendisk *disk = dev_to_disk(dev);
+
+	return sprintf(buf, "%d\n", disk->policy ? 1 : 0);
+}
+
 static ssize_t disk_size_show(struct device *dev,
 			      struct device_attribute *attr, char *buf)
 {
@@ -472,6 +482,7 @@
 
 static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL);
 static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL);
+static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL);
 static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL);
 static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
 static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL);
@@ -483,6 +494,7 @@
 static struct attribute *disk_attrs[] = {
 	&dev_attr_range.attr,
 	&dev_attr_removable.attr,
+	&dev_attr_ro.attr,
 	&dev_attr_size.attr,
 	&dev_attr_capability.attr,
 	&dev_attr_stat.attr,
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index 78199c0..c5b9bcf 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -105,120 +105,12 @@
 	return put_user(1, p);
 }
 
-#define CMD_READ_SAFE	0x01
-#define CMD_WRITE_SAFE	0x02
-#define CMD_WARNED	0x04
-#define safe_for_read(cmd)	[cmd] = CMD_READ_SAFE
-#define safe_for_write(cmd)	[cmd] = CMD_WRITE_SAFE
-
-int blk_verify_command(unsigned char *cmd, int has_write_perm)
-{
-	static unsigned char cmd_type[256] = {
-
-		/* Basic read-only commands */
-		safe_for_read(TEST_UNIT_READY),
-		safe_for_read(REQUEST_SENSE),
-		safe_for_read(READ_6),
-		safe_for_read(READ_10),
-		safe_for_read(READ_12),
-		safe_for_read(READ_16),
-		safe_for_read(READ_BUFFER),
-		safe_for_read(READ_DEFECT_DATA),
-		safe_for_read(READ_LONG),
-		safe_for_read(INQUIRY),
-		safe_for_read(MODE_SENSE),
-		safe_for_read(MODE_SENSE_10),
-		safe_for_read(LOG_SENSE),
-		safe_for_read(START_STOP),
-		safe_for_read(GPCMD_VERIFY_10),
-		safe_for_read(VERIFY_16),
-
-		/* Audio CD commands */
-		safe_for_read(GPCMD_PLAY_CD),
-		safe_for_read(GPCMD_PLAY_AUDIO_10),
-		safe_for_read(GPCMD_PLAY_AUDIO_MSF),
-		safe_for_read(GPCMD_PLAY_AUDIO_TI),
-		safe_for_read(GPCMD_PAUSE_RESUME),
-
-		/* CD/DVD data reading */
-		safe_for_read(GPCMD_READ_BUFFER_CAPACITY),
-		safe_for_read(GPCMD_READ_CD),
-		safe_for_read(GPCMD_READ_CD_MSF),
-		safe_for_read(GPCMD_READ_DISC_INFO),
-		safe_for_read(GPCMD_READ_CDVD_CAPACITY),
-		safe_for_read(GPCMD_READ_DVD_STRUCTURE),
-		safe_for_read(GPCMD_READ_HEADER),
-		safe_for_read(GPCMD_READ_TRACK_RZONE_INFO),
-		safe_for_read(GPCMD_READ_SUBCHANNEL),
-		safe_for_read(GPCMD_READ_TOC_PMA_ATIP),
-		safe_for_read(GPCMD_REPORT_KEY),
-		safe_for_read(GPCMD_SCAN),
-		safe_for_read(GPCMD_GET_CONFIGURATION),
-		safe_for_read(GPCMD_READ_FORMAT_CAPACITIES),
-		safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION),
-		safe_for_read(GPCMD_GET_PERFORMANCE),
-		safe_for_read(GPCMD_SEEK),
-		safe_for_read(GPCMD_STOP_PLAY_SCAN),
-
-		/* Basic writing commands */
-		safe_for_write(WRITE_6),
-		safe_for_write(WRITE_10),
-		safe_for_write(WRITE_VERIFY),
-		safe_for_write(WRITE_12),
-		safe_for_write(WRITE_VERIFY_12),
-		safe_for_write(WRITE_16),
-		safe_for_write(WRITE_LONG),
-		safe_for_write(WRITE_LONG_2),
-		safe_for_write(ERASE),
-		safe_for_write(GPCMD_MODE_SELECT_10),
-		safe_for_write(MODE_SELECT),
-		safe_for_write(LOG_SELECT),
-		safe_for_write(GPCMD_BLANK),
-		safe_for_write(GPCMD_CLOSE_TRACK),
-		safe_for_write(GPCMD_FLUSH_CACHE),
-		safe_for_write(GPCMD_FORMAT_UNIT),
-		safe_for_write(GPCMD_REPAIR_RZONE_TRACK),
-		safe_for_write(GPCMD_RESERVE_RZONE_TRACK),
-		safe_for_write(GPCMD_SEND_DVD_STRUCTURE),
-		safe_for_write(GPCMD_SEND_EVENT),
-		safe_for_write(GPCMD_SEND_KEY),
-		safe_for_write(GPCMD_SEND_OPC),
-		safe_for_write(GPCMD_SEND_CUE_SHEET),
-		safe_for_write(GPCMD_SET_SPEED),
-		safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL),
-		safe_for_write(GPCMD_LOAD_UNLOAD),
-		safe_for_write(GPCMD_SET_STREAMING),
-	};
-	unsigned char type = cmd_type[cmd[0]];
-
-	/* Anybody who can open the device can do a read-safe command */
-	if (type & CMD_READ_SAFE)
-		return 0;
-
-	/* Write-safe commands just require a writable open.. */
-	if ((type & CMD_WRITE_SAFE) && has_write_perm)
-		return 0;
-
-	/* And root can do any command.. */
-	if (capable(CAP_SYS_RAWIO))
-		return 0;
-
-	if (!type) {
-		cmd_type[cmd[0]] = CMD_WARNED;
-		printk(KERN_WARNING "scsi: unknown opcode 0x%02x\n", cmd[0]);
-	}
-
-	/* Otherwise fail it with an "Operation not permitted" */
-	return -EPERM;
-}
-EXPORT_SYMBOL_GPL(blk_verify_command);
-
 static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq,
-			     struct sg_io_hdr *hdr, int has_write_perm)
+			     struct sg_io_hdr *hdr, struct file *file)
 {
 	if (copy_from_user(rq->cmd, hdr->cmdp, hdr->cmd_len))
 		return -EFAULT;
-	if (blk_verify_command(rq->cmd, has_write_perm))
+	if (blk_verify_command(file, rq->cmd))
 		return -EPERM;
 
 	/*
@@ -287,7 +179,7 @@
 		struct gendisk *bd_disk, struct sg_io_hdr *hdr)
 {
 	unsigned long start_time;
-	int writing = 0, ret = 0, has_write_perm = 0;
+	int writing = 0, ret = 0;
 	struct request *rq;
 	char sense[SCSI_SENSE_BUFFERSIZE];
 	struct bio *bio;
@@ -316,10 +208,7 @@
 	if (!rq)
 		return -ENOMEM;
 
-	if (file)
-		has_write_perm = file->f_mode & FMODE_WRITE;
-
-	if (blk_fill_sghdr_rq(q, rq, hdr, has_write_perm)) {
+	if (blk_fill_sghdr_rq(q, rq, hdr, file)) {
 		blk_put_request(rq);
 		return -EFAULT;
 	}
@@ -451,7 +340,7 @@
 	if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len))
 		goto error;
 
-	err = blk_verify_command(rq->cmd, file->f_mode & FMODE_WRITE);
+	err = blk_verify_command(file, rq->cmd);
 	if (err)
 		goto error;
 
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 864456c..ea50357 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -65,6 +65,7 @@
 config CRYPTO_CRYPTD
 	tristate "Software async crypto daemon"
 	select CRYPTO_BLKCIPHER
+	select CRYPTO_HASH
 	select CRYPTO_MANAGER
 	help
 	  This is a generic software asynchronous crypto daemon that
@@ -212,7 +213,7 @@
 
 config CRYPTO_CRC32C
 	tristate "CRC32c CRC algorithm"
-	select CRYPTO_ALGAPI
+	select CRYPTO_HASH
 	select LIBCRC32C
 	help
 	  Castagnoli, et al Cyclic Redundancy-Check Algorithm.  Used
@@ -241,6 +242,57 @@
 	  should not be used for other purposes because of the weakness
 	  of the algorithm.
 
+config CRYPTO_RMD128
+  tristate "RIPEMD-128 digest algorithm"
+  select CRYPTO_ALGAPI
+  help
+    RIPEMD-128 (ISO/IEC 10118-3:2004).
+
+    RIPEMD-128 is a 128-bit cryptographic hash function. It should only
+    to be used as a secure replacement for RIPEMD. For other use cases
+    RIPEMD-160 should be used.
+
+    Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+    See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+
+config CRYPTO_RMD160
+  tristate "RIPEMD-160 digest algorithm"
+  select CRYPTO_ALGAPI
+  help
+    RIPEMD-160 (ISO/IEC 10118-3:2004).
+
+    RIPEMD-160 is a 160-bit cryptographic hash function. It is intended
+    to be used as a secure replacement for the 128-bit hash functions
+    MD4, MD5 and it's predecessor RIPEMD (not to be confused with RIPEMD-128).
+
+    It's speed is comparable to SHA1 and there are no known attacks against
+    RIPEMD-160.
+
+    Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+    See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+
+config CRYPTO_RMD256
+  tristate "RIPEMD-256 digest algorithm"
+  select CRYPTO_ALGAPI
+  help
+    RIPEMD-256 is an optional extension of RIPEMD-128 with a 256 bit hash.
+    It is intended for applications that require longer hash-results, without
+    needing a larger security level (than RIPEMD-128).
+
+    Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+    See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+
+config CRYPTO_RMD320
+  tristate "RIPEMD-320 digest algorithm"
+  select CRYPTO_ALGAPI
+  help
+    RIPEMD-320 is an optional extension of RIPEMD-160 with a 320 bit hash.
+    It is intended for applications that require longer hash-results, without
+    needing a larger security level (than RIPEMD-160).
+
+    Developed by Hans Dobbertin, Antoon Bosselaers and Bart Preneel.
+    See <http://home.esat.kuleuven.be/~bosselae/ripemd160.html>
+
 config CRYPTO_SHA1
 	tristate "SHA1 digest algorithm"
 	select CRYPTO_ALGAPI
@@ -614,6 +666,15 @@
 	help
 	  This is the LZO algorithm.
 
+comment "Random Number Generation"
+
+config CRYPTO_PRNG
+	tristate "Pseudo Random Number Generation for Cryptographic modules"
+	help
+	  This option enables the generic pseudo random number generator
+	  for cryptographic modules.  Uses the Algorithm specified in
+	  ANSI X9.31 A.2.4
+
 source "drivers/crypto/Kconfig"
 
 endif	# if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index ca02441..ef61b3b 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -19,6 +19,7 @@
 obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
 
 crypto_hash-objs := hash.o
+crypto_hash-objs += ahash.o
 obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o
 
 obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
@@ -27,6 +28,10 @@
 obj-$(CONFIG_CRYPTO_NULL) += crypto_null.o
 obj-$(CONFIG_CRYPTO_MD4) += md4.o
 obj-$(CONFIG_CRYPTO_MD5) += md5.o
+obj-$(CONFIG_CRYPTO_RMD128) += rmd128.o
+obj-$(CONFIG_CRYPTO_RMD160) += rmd160.o
+obj-$(CONFIG_CRYPTO_RMD256) += rmd256.o
+obj-$(CONFIG_CRYPTO_RMD320) += rmd320.o
 obj-$(CONFIG_CRYPTO_SHA1) += sha1_generic.o
 obj-$(CONFIG_CRYPTO_SHA256) += sha256_generic.o
 obj-$(CONFIG_CRYPTO_SHA512) += sha512_generic.o
@@ -64,7 +69,7 @@
 obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
 obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o
 obj-$(CONFIG_CRYPTO_LZO) += lzo.o
-
+obj-$(CONFIG_CRYPTO_PRNG) += prng.o
 obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
 
 #
diff --git a/crypto/ahash.c b/crypto/ahash.c
new file mode 100644
index 0000000..27128f2
--- /dev/null
+++ b/crypto/ahash.c
@@ -0,0 +1,194 @@
+/*
+ * Asynchronous Cryptographic Hash operations.
+ *
+ * This is the asynchronous version of hash.c with notification of
+ * completion via a callback.
+ *
+ * Copyright (c) 2008 Loc Ho <lho@amcc.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 <crypto/internal/hash.h>
+#include <crypto/scatterwalk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+
+#include "internal.h"
+
+static int hash_walk_next(struct crypto_hash_walk *walk)
+{
+	unsigned int alignmask = walk->alignmask;
+	unsigned int offset = walk->offset;
+	unsigned int nbytes = min(walk->entrylen,
+				  ((unsigned int)(PAGE_SIZE)) - offset);
+
+	walk->data = crypto_kmap(walk->pg, 0);
+	walk->data += offset;
+
+	if (offset & alignmask)
+		nbytes = alignmask + 1 - (offset & alignmask);
+
+	walk->entrylen -= nbytes;
+	return nbytes;
+}
+
+static int hash_walk_new_entry(struct crypto_hash_walk *walk)
+{
+	struct scatterlist *sg;
+
+	sg = walk->sg;
+	walk->pg = sg_page(sg);
+	walk->offset = sg->offset;
+	walk->entrylen = sg->length;
+
+	if (walk->entrylen > walk->total)
+		walk->entrylen = walk->total;
+	walk->total -= walk->entrylen;
+
+	return hash_walk_next(walk);
+}
+
+int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err)
+{
+	unsigned int alignmask = walk->alignmask;
+	unsigned int nbytes = walk->entrylen;
+
+	walk->data -= walk->offset;
+
+	if (nbytes && walk->offset & alignmask && !err) {
+		walk->offset += alignmask - 1;
+		walk->offset = ALIGN(walk->offset, alignmask + 1);
+		walk->data += walk->offset;
+
+		nbytes = min(nbytes,
+			     ((unsigned int)(PAGE_SIZE)) - walk->offset);
+		walk->entrylen -= nbytes;
+
+		return nbytes;
+	}
+
+	crypto_kunmap(walk->data, 0);
+	crypto_yield(walk->flags);
+
+	if (err)
+		return err;
+
+	walk->offset = 0;
+
+	if (nbytes)
+		return hash_walk_next(walk);
+
+	if (!walk->total)
+		return 0;
+
+	walk->sg = scatterwalk_sg_next(walk->sg);
+
+	return hash_walk_new_entry(walk);
+}
+EXPORT_SYMBOL_GPL(crypto_hash_walk_done);
+
+int crypto_hash_walk_first(struct ahash_request *req,
+			   struct crypto_hash_walk *walk)
+{
+	walk->total = req->nbytes;
+
+	if (!walk->total)
+		return 0;
+
+	walk->alignmask = crypto_ahash_alignmask(crypto_ahash_reqtfm(req));
+	walk->sg = req->src;
+	walk->flags = req->base.flags;
+
+	return hash_walk_new_entry(walk);
+}
+EXPORT_SYMBOL_GPL(crypto_hash_walk_first);
+
+static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key,
+				unsigned int keylen)
+{
+	struct ahash_alg *ahash = crypto_ahash_alg(tfm);
+	unsigned long alignmask = crypto_ahash_alignmask(tfm);
+	int ret;
+	u8 *buffer, *alignbuffer;
+	unsigned long absize;
+
+	absize = keylen + alignmask;
+	buffer = kmalloc(absize, GFP_ATOMIC);
+	if (!buffer)
+		return -ENOMEM;
+
+	alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
+	memcpy(alignbuffer, key, keylen);
+	ret = ahash->setkey(tfm, alignbuffer, keylen);
+	memset(alignbuffer, 0, keylen);
+	kfree(buffer);
+	return ret;
+}
+
+static int ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
+			unsigned int keylen)
+{
+	struct ahash_alg *ahash = crypto_ahash_alg(tfm);
+	unsigned long alignmask = crypto_ahash_alignmask(tfm);
+
+	if ((unsigned long)key & alignmask)
+		return ahash_setkey_unaligned(tfm, key, keylen);
+
+	return ahash->setkey(tfm, key, keylen);
+}
+
+static unsigned int crypto_ahash_ctxsize(struct crypto_alg *alg, u32 type,
+					u32 mask)
+{
+	return alg->cra_ctxsize;
+}
+
+static int crypto_init_ahash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+	struct ahash_alg *alg = &tfm->__crt_alg->cra_ahash;
+	struct ahash_tfm *crt   = &tfm->crt_ahash;
+
+	if (alg->digestsize > PAGE_SIZE / 8)
+		return -EINVAL;
+
+	crt->init = alg->init;
+	crt->update = alg->update;
+	crt->final  = alg->final;
+	crt->digest = alg->digest;
+	crt->setkey = ahash_setkey;
+	crt->digestsize = alg->digestsize;
+
+	return 0;
+}
+
+static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg)
+	__attribute__ ((unused));
+static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg)
+{
+	seq_printf(m, "type         : ahash\n");
+	seq_printf(m, "async        : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
+					     "yes" : "no");
+	seq_printf(m, "blocksize    : %u\n", alg->cra_blocksize);
+	seq_printf(m, "digestsize   : %u\n", alg->cra_hash.digestsize);
+}
+
+const struct crypto_type crypto_ahash_type = {
+	.ctxsize = crypto_ahash_ctxsize,
+	.init = crypto_init_ahash_ops,
+#ifdef CONFIG_PROC_FS
+	.show = crypto_ahash_show,
+#endif
+};
+EXPORT_SYMBOL_GPL(crypto_ahash_type);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Asynchronous cryptographic hash type");
diff --git a/crypto/api.c b/crypto/api.c
index 0a0f41e..d06e332 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -235,8 +235,12 @@
 		return crypto_init_cipher_ops(tfm);
 		
 	case CRYPTO_ALG_TYPE_DIGEST:
-		return crypto_init_digest_ops(tfm);
-		
+		if ((mask & CRYPTO_ALG_TYPE_HASH_MASK) !=
+		    CRYPTO_ALG_TYPE_HASH_MASK)
+			return crypto_init_digest_ops_async(tfm);
+		else
+			return crypto_init_digest_ops(tfm);
+
 	case CRYPTO_ALG_TYPE_COMPRESS:
 		return crypto_init_compress_ops(tfm);
 	
diff --git a/crypto/camellia.c b/crypto/camellia.c
index 493fee7..b1cc4de 100644
--- a/crypto/camellia.c
+++ b/crypto/camellia.c
@@ -35,6 +35,8 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/bitops.h>
+#include <asm/unaligned.h>
 
 static const u32 camellia_sp1110[256] = {
 	0x70707000,0x82828200,0x2c2c2c00,0xececec00,
@@ -335,20 +337,6 @@
 /*
  *  macros
  */
-#define GETU32(v, pt) \
-    do { \
-	/* latest breed of gcc is clever enough to use move */ \
-	memcpy(&(v), (pt), 4); \
-	(v) = be32_to_cpu(v); \
-    } while(0)
-
-/* rotation right shift 1byte */
-#define ROR8(x) (((x) >> 8) + ((x) << 24))
-/* rotation left shift 1bit */
-#define ROL1(x) (((x) << 1) + ((x) >> 31))
-/* rotation left shift 1byte */
-#define ROL8(x) (((x) << 8) + ((x) >> 24))
-
 #define ROLDQ(ll, lr, rl, rr, w0, w1, bits)		\
     do {						\
 	w0 = ll;					\
@@ -383,7 +371,7 @@
 	   ^ camellia_sp3033[(u8)(il >> 8)]			\
 	   ^ camellia_sp4404[(u8)(il     )];			\
 	yl ^= yr;						\
-	yr = ROR8(yr);						\
+	yr = ror32(yr, 8);					\
 	yr ^= yl;						\
     } while(0)
 
@@ -405,7 +393,7 @@
 	subL[7] ^= subL[1]; subR[7] ^= subR[1];
 	subL[1] ^= subR[1] & ~subR[9];
 	dw = subL[1] & subL[9],
-		subR[1] ^= ROL1(dw); /* modified for FLinv(kl2) */
+		subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl2) */
 	/* round 8 */
 	subL[11] ^= subL[1]; subR[11] ^= subR[1];
 	/* round 10 */
@@ -414,7 +402,7 @@
 	subL[15] ^= subL[1]; subR[15] ^= subR[1];
 	subL[1] ^= subR[1] & ~subR[17];
 	dw = subL[1] & subL[17],
-		subR[1] ^= ROL1(dw); /* modified for FLinv(kl4) */
+		subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl4) */
 	/* round 14 */
 	subL[19] ^= subL[1]; subR[19] ^= subR[1];
 	/* round 16 */
@@ -430,7 +418,7 @@
 	} else {
 		subL[1] ^= subR[1] & ~subR[25];
 		dw = subL[1] & subL[25],
-			subR[1] ^= ROL1(dw); /* modified for FLinv(kl6) */
+			subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl6) */
 		/* round 20 */
 		subL[27] ^= subL[1]; subR[27] ^= subR[1];
 		/* round 22 */
@@ -450,7 +438,7 @@
 		subL[26] ^= kw4l; subR[26] ^= kw4r;
 		kw4l ^= kw4r & ~subR[24];
 		dw = kw4l & subL[24],
-			kw4r ^= ROL1(dw); /* modified for FL(kl5) */
+			kw4r ^= rol32(dw, 1); /* modified for FL(kl5) */
 	}
 	/* round 17 */
 	subL[22] ^= kw4l; subR[22] ^= kw4r;
@@ -460,7 +448,7 @@
 	subL[18] ^= kw4l; subR[18] ^= kw4r;
 	kw4l ^= kw4r & ~subR[16];
 	dw = kw4l & subL[16],
-		kw4r ^= ROL1(dw); /* modified for FL(kl3) */
+		kw4r ^= rol32(dw, 1); /* modified for FL(kl3) */
 	/* round 11 */
 	subL[14] ^= kw4l; subR[14] ^= kw4r;
 	/* round 9 */
@@ -469,7 +457,7 @@
 	subL[10] ^= kw4l; subR[10] ^= kw4r;
 	kw4l ^= kw4r & ~subR[8];
 	dw = kw4l & subL[8],
-		kw4r ^= ROL1(dw); /* modified for FL(kl1) */
+		kw4r ^= rol32(dw, 1); /* modified for FL(kl1) */
 	/* round 5 */
 	subL[6] ^= kw4l; subR[6] ^= kw4r;
 	/* round 3 */
@@ -494,7 +482,7 @@
 	SUBKEY_R(6) = subR[5] ^ subR[7];
 	tl = subL[10] ^ (subR[10] & ~subR[8]);
 	dw = tl & subL[8],  /* FL(kl1) */
-		tr = subR[10] ^ ROL1(dw);
+		tr = subR[10] ^ rol32(dw, 1);
 	SUBKEY_L(7) = subL[6] ^ tl; /* round 6 */
 	SUBKEY_R(7) = subR[6] ^ tr;
 	SUBKEY_L(8) = subL[8];       /* FL(kl1) */
@@ -503,7 +491,7 @@
 	SUBKEY_R(9) = subR[9];
 	tl = subL[7] ^ (subR[7] & ~subR[9]);
 	dw = tl & subL[9],  /* FLinv(kl2) */
-		tr = subR[7] ^ ROL1(dw);
+		tr = subR[7] ^ rol32(dw, 1);
 	SUBKEY_L(10) = tl ^ subL[11]; /* round 7 */
 	SUBKEY_R(10) = tr ^ subR[11];
 	SUBKEY_L(11) = subL[10] ^ subL[12]; /* round 8 */
@@ -516,7 +504,7 @@
 	SUBKEY_R(14) = subR[13] ^ subR[15];
 	tl = subL[18] ^ (subR[18] & ~subR[16]);
 	dw = tl & subL[16], /* FL(kl3) */
-		tr = subR[18] ^ ROL1(dw);
+		tr = subR[18] ^ rol32(dw, 1);
 	SUBKEY_L(15) = subL[14] ^ tl; /* round 12 */
 	SUBKEY_R(15) = subR[14] ^ tr;
 	SUBKEY_L(16) = subL[16];     /* FL(kl3) */
@@ -525,7 +513,7 @@
 	SUBKEY_R(17) = subR[17];
 	tl = subL[15] ^ (subR[15] & ~subR[17]);
 	dw = tl & subL[17], /* FLinv(kl4) */
-		tr = subR[15] ^ ROL1(dw);
+		tr = subR[15] ^ rol32(dw, 1);
 	SUBKEY_L(18) = tl ^ subL[19]; /* round 13 */
 	SUBKEY_R(18) = tr ^ subR[19];
 	SUBKEY_L(19) = subL[18] ^ subL[20]; /* round 14 */
@@ -544,7 +532,7 @@
 	} else {
 		tl = subL[26] ^ (subR[26] & ~subR[24]);
 		dw = tl & subL[24], /* FL(kl5) */
-			tr = subR[26] ^ ROL1(dw);
+			tr = subR[26] ^ rol32(dw, 1);
 		SUBKEY_L(23) = subL[22] ^ tl; /* round 18 */
 		SUBKEY_R(23) = subR[22] ^ tr;
 		SUBKEY_L(24) = subL[24];     /* FL(kl5) */
@@ -553,7 +541,7 @@
 		SUBKEY_R(25) = subR[25];
 		tl = subL[23] ^ (subR[23] & ~subR[25]);
 		dw = tl & subL[25], /* FLinv(kl6) */
-			tr = subR[23] ^ ROL1(dw);
+			tr = subR[23] ^ rol32(dw, 1);
 		SUBKEY_L(26) = tl ^ subL[27]; /* round 19 */
 		SUBKEY_R(26) = tr ^ subR[27];
 		SUBKEY_L(27) = subL[26] ^ subL[28]; /* round 20 */
@@ -573,17 +561,17 @@
 	/* apply the inverse of the last half of P-function */
 	i = 2;
 	do {
-		dw = SUBKEY_L(i + 0) ^ SUBKEY_R(i + 0); dw = ROL8(dw);/* round 1 */
+		dw = SUBKEY_L(i + 0) ^ SUBKEY_R(i + 0); dw = rol32(dw, 8);/* round 1 */
 		SUBKEY_R(i + 0) = SUBKEY_L(i + 0) ^ dw; SUBKEY_L(i + 0) = dw;
-		dw = SUBKEY_L(i + 1) ^ SUBKEY_R(i + 1); dw = ROL8(dw);/* round 2 */
+		dw = SUBKEY_L(i + 1) ^ SUBKEY_R(i + 1); dw = rol32(dw, 8);/* round 2 */
 		SUBKEY_R(i + 1) = SUBKEY_L(i + 1) ^ dw; SUBKEY_L(i + 1) = dw;
-		dw = SUBKEY_L(i + 2) ^ SUBKEY_R(i + 2); dw = ROL8(dw);/* round 3 */
+		dw = SUBKEY_L(i + 2) ^ SUBKEY_R(i + 2); dw = rol32(dw, 8);/* round 3 */
 		SUBKEY_R(i + 2) = SUBKEY_L(i + 2) ^ dw; SUBKEY_L(i + 2) = dw;
-		dw = SUBKEY_L(i + 3) ^ SUBKEY_R(i + 3); dw = ROL8(dw);/* round 4 */
+		dw = SUBKEY_L(i + 3) ^ SUBKEY_R(i + 3); dw = rol32(dw, 8);/* round 4 */
 		SUBKEY_R(i + 3) = SUBKEY_L(i + 3) ^ dw; SUBKEY_L(i + 3) = dw;
-		dw = SUBKEY_L(i + 4) ^ SUBKEY_R(i + 4); dw = ROL8(dw);/* round 5 */
+		dw = SUBKEY_L(i + 4) ^ SUBKEY_R(i + 4); dw = rol32(dw, 9);/* round 5 */
 		SUBKEY_R(i + 4) = SUBKEY_L(i + 4) ^ dw; SUBKEY_L(i + 4) = dw;
-		dw = SUBKEY_L(i + 5) ^ SUBKEY_R(i + 5); dw = ROL8(dw);/* round 6 */
+		dw = SUBKEY_L(i + 5) ^ SUBKEY_R(i + 5); dw = rol32(dw, 8);/* round 6 */
 		SUBKEY_R(i + 5) = SUBKEY_L(i + 5) ^ dw; SUBKEY_L(i + 5) = dw;
 		i += 8;
 	} while (i < max);
@@ -599,10 +587,10 @@
 	/**
 	 *  k == kll || klr || krl || krr (|| is concatenation)
 	 */
-	GETU32(kll, key     );
-	GETU32(klr, key +  4);
-	GETU32(krl, key +  8);
-	GETU32(krr, key + 12);
+	kll = get_unaligned_be32(key);
+	klr = get_unaligned_be32(key + 4);
+	krl = get_unaligned_be32(key + 8);
+	krr = get_unaligned_be32(key + 12);
 
 	/* generate KL dependent subkeys */
 	/* kw1 */
@@ -707,14 +695,14 @@
 	 *  key = (kll || klr || krl || krr || krll || krlr || krrl || krrr)
 	 *  (|| is concatenation)
 	 */
-	GETU32(kll,  key     );
-	GETU32(klr,  key +  4);
-	GETU32(krl,  key +  8);
-	GETU32(krr,  key + 12);
-	GETU32(krll, key + 16);
-	GETU32(krlr, key + 20);
-	GETU32(krrl, key + 24);
-	GETU32(krrr, key + 28);
+	kll = get_unaligned_be32(key);
+	klr = get_unaligned_be32(key + 4);
+	krl = get_unaligned_be32(key + 8);
+	krr = get_unaligned_be32(key + 12);
+	krll = get_unaligned_be32(key + 16);
+	krlr = get_unaligned_be32(key + 20);
+	krrl = get_unaligned_be32(key + 24);
+	krrr = get_unaligned_be32(key + 28);
 
 	/* generate KL dependent subkeys */
 	/* kw1 */
@@ -870,13 +858,13 @@
 	t0 &= ll;							\
 	t2 |= rr;							\
 	rl ^= t2;							\
-	lr ^= ROL1(t0);							\
+	lr ^= rol32(t0, 1);						\
 	t3 = krl;							\
 	t1 = klr;							\
 	t3 &= rl;							\
 	t1 |= lr;							\
 	ll ^= t1;							\
-	rr ^= ROL1(t3);							\
+	rr ^= rol32(t3, 1);						\
     } while(0)
 
 #define CAMELLIA_ROUNDSM(xl, xr, kl, kr, yl, yr, il, ir)		\
@@ -892,7 +880,7 @@
 	il ^= kl;							\
 	ir ^= il ^ kr;							\
 	yl ^= ir;							\
-	yr ^= ROR8(il) ^ ir;						\
+	yr ^= ror32(il, 8) ^ ir;						\
     } while(0)
 
 /* max = 24: 128bit encrypt, max = 32: 256bit encrypt */
diff --git a/crypto/crc32c.c b/crypto/crc32c.c
index 0dcf64a..a882d9e 100644
--- a/crypto/crc32c.c
+++ b/crypto/crc32c.c
@@ -5,20 +5,23 @@
  *
  * This module file is a wrapper to invoke the lib/crc32c routines.
  *
+ * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
+ *
  * 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 <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/string.h>
-#include <linux/crypto.h>
 #include <linux/crc32c.h>
 #include <linux/kernel.h>
 
-#define CHKSUM_BLOCK_SIZE	32
+#define CHKSUM_BLOCK_SIZE	1
 #define CHKSUM_DIGEST_SIZE	4
 
 struct chksum_ctx {
@@ -71,7 +74,7 @@
 	*(__le32 *)out = ~cpu_to_le32(mctx->crc);
 }
 
-static int crc32c_cra_init(struct crypto_tfm *tfm)
+static int crc32c_cra_init_old(struct crypto_tfm *tfm)
 {
 	struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
 
@@ -79,14 +82,14 @@
 	return 0;
 }
 
-static struct crypto_alg alg = {
+static struct crypto_alg old_alg = {
 	.cra_name	=	"crc32c",
 	.cra_flags	=	CRYPTO_ALG_TYPE_DIGEST,
 	.cra_blocksize	=	CHKSUM_BLOCK_SIZE,
 	.cra_ctxsize	=	sizeof(struct chksum_ctx),
 	.cra_module	=	THIS_MODULE,
-	.cra_list	=	LIST_HEAD_INIT(alg.cra_list),
-	.cra_init	=	crc32c_cra_init,
+	.cra_list	=	LIST_HEAD_INIT(old_alg.cra_list),
+	.cra_init	=	crc32c_cra_init_old,
 	.cra_u		=	{
 		.digest = {
 			 .dia_digestsize=	CHKSUM_DIGEST_SIZE,
@@ -98,14 +101,125 @@
 	}
 };
 
+/*
+ * Setting the seed allows arbitrary accumulators and flexible XOR policy
+ * If your algorithm starts with ~0, then XOR with ~0 before you set
+ * the seed.
+ */
+static int crc32c_setkey(struct crypto_ahash *hash, const u8 *key,
+			 unsigned int keylen)
+{
+	u32 *mctx = crypto_ahash_ctx(hash);
+
+	if (keylen != sizeof(u32)) {
+		crypto_ahash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+		return -EINVAL;
+	}
+	*mctx = le32_to_cpup((__le32 *)key);
+	return 0;
+}
+
+static int crc32c_init(struct ahash_request *req)
+{
+	u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
+	u32 *crcp = ahash_request_ctx(req);
+
+	*crcp = *mctx;
+	return 0;
+}
+
+static int crc32c_update(struct ahash_request *req)
+{
+	struct crypto_hash_walk walk;
+	u32 *crcp = ahash_request_ctx(req);
+	u32 crc = *crcp;
+	int nbytes;
+
+	for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
+	     nbytes = crypto_hash_walk_done(&walk, 0))
+		crc = crc32c(crc, walk.data, nbytes);
+
+	*crcp = crc;
+	return 0;
+}
+
+static int crc32c_final(struct ahash_request *req)
+{
+	u32 *crcp = ahash_request_ctx(req);
+	
+	*(__le32 *)req->result = ~cpu_to_le32p(crcp);
+	return 0;
+}
+
+static int crc32c_digest(struct ahash_request *req)
+{
+	struct crypto_hash_walk walk;
+	u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
+	u32 crc = *mctx;
+	int nbytes;
+
+	for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
+	     nbytes = crypto_hash_walk_done(&walk, 0))
+		crc = crc32c(crc, walk.data, nbytes);
+
+	*(__le32 *)req->result = ~cpu_to_le32(crc);
+	return 0;
+}
+
+static int crc32c_cra_init(struct crypto_tfm *tfm)
+{
+	u32 *key = crypto_tfm_ctx(tfm);
+
+	*key = ~0;
+
+	tfm->crt_ahash.reqsize = sizeof(u32);
+
+	return 0;
+}
+
+static struct crypto_alg alg = {
+	.cra_name		=	"crc32c",
+	.cra_driver_name	=	"crc32c-generic",
+	.cra_priority		=	100,
+	.cra_flags		=	CRYPTO_ALG_TYPE_AHASH,
+	.cra_blocksize		=	CHKSUM_BLOCK_SIZE,
+	.cra_alignmask		=	3,
+	.cra_ctxsize		=	sizeof(u32),
+	.cra_module		=	THIS_MODULE,
+	.cra_list		=	LIST_HEAD_INIT(alg.cra_list),
+	.cra_init		=	crc32c_cra_init,
+	.cra_type		=	&crypto_ahash_type,
+	.cra_u			=	{
+		.ahash = {
+			 .digestsize	=	CHKSUM_DIGEST_SIZE,
+			 .setkey	=	crc32c_setkey,
+			 .init   	= 	crc32c_init,
+			 .update 	=	crc32c_update,
+			 .final  	=	crc32c_final,
+			 .digest  	=	crc32c_digest,
+		 }
+	}
+};
+
 static int __init crc32c_mod_init(void)
 {
-	return crypto_register_alg(&alg);
+	int err;
+
+	err = crypto_register_alg(&old_alg);
+	if (err)
+		return err;
+
+	err = crypto_register_alg(&alg);
+	if (err)
+		crypto_unregister_alg(&old_alg);
+
+	return err;
 }
 
 static void __exit crc32c_mod_fini(void)
 {
 	crypto_unregister_alg(&alg);
+	crypto_unregister_alg(&old_alg);
 }
 
 module_init(crc32c_mod_init);
diff --git a/crypto/cryptd.c b/crypto/cryptd.c
index b150de5..d29e06b 100644
--- a/crypto/cryptd.c
+++ b/crypto/cryptd.c
@@ -11,6 +11,7 @@
  */
 
 #include <crypto/algapi.h>
+#include <crypto/internal/hash.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -45,6 +46,13 @@
 	crypto_completion_t complete;
 };
 
+struct cryptd_hash_ctx {
+	struct crypto_hash *child;
+};
+
+struct cryptd_hash_request_ctx {
+	crypto_completion_t complete;
+};
 
 static inline struct cryptd_state *cryptd_get_state(struct crypto_tfm *tfm)
 {
@@ -82,10 +90,8 @@
 
 	rctx = ablkcipher_request_ctx(req);
 
-	if (unlikely(err == -EINPROGRESS)) {
-		rctx->complete(&req->base, err);
-		return;
-	}
+	if (unlikely(err == -EINPROGRESS))
+		goto out;
 
 	desc.tfm = child;
 	desc.info = req->info;
@@ -95,8 +101,9 @@
 
 	req->base.complete = rctx->complete;
 
+out:
 	local_bh_disable();
-	req->base.complete(&req->base, err);
+	rctx->complete(&req->base, err);
 	local_bh_enable();
 }
 
@@ -261,6 +268,240 @@
 	return inst;
 }
 
+static int cryptd_hash_init_tfm(struct crypto_tfm *tfm)
+{
+	struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+	struct cryptd_instance_ctx *ictx = crypto_instance_ctx(inst);
+	struct crypto_spawn *spawn = &ictx->spawn;
+	struct cryptd_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypto_hash *cipher;
+
+	cipher = crypto_spawn_hash(spawn);
+	if (IS_ERR(cipher))
+		return PTR_ERR(cipher);
+
+	ctx->child = cipher;
+	tfm->crt_ahash.reqsize =
+		sizeof(struct cryptd_hash_request_ctx);
+	return 0;
+}
+
+static void cryptd_hash_exit_tfm(struct crypto_tfm *tfm)
+{
+	struct cryptd_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct cryptd_state *state = cryptd_get_state(tfm);
+	int active;
+
+	mutex_lock(&state->mutex);
+	active = ahash_tfm_in_queue(&state->queue,
+				__crypto_ahash_cast(tfm));
+	mutex_unlock(&state->mutex);
+
+	BUG_ON(active);
+
+	crypto_free_hash(ctx->child);
+}
+
+static int cryptd_hash_setkey(struct crypto_ahash *parent,
+				   const u8 *key, unsigned int keylen)
+{
+	struct cryptd_hash_ctx *ctx   = crypto_ahash_ctx(parent);
+	struct crypto_hash     *child = ctx->child;
+	int err;
+
+	crypto_hash_clear_flags(child, CRYPTO_TFM_REQ_MASK);
+	crypto_hash_set_flags(child, crypto_ahash_get_flags(parent) &
+					  CRYPTO_TFM_REQ_MASK);
+	err = crypto_hash_setkey(child, key, keylen);
+	crypto_ahash_set_flags(parent, crypto_hash_get_flags(child) &
+					    CRYPTO_TFM_RES_MASK);
+	return err;
+}
+
+static int cryptd_hash_enqueue(struct ahash_request *req,
+				crypto_completion_t complete)
+{
+	struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req);
+	struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+	struct cryptd_state *state =
+		cryptd_get_state(crypto_ahash_tfm(tfm));
+	int err;
+
+	rctx->complete = req->base.complete;
+	req->base.complete = complete;
+
+	spin_lock_bh(&state->lock);
+	err = ahash_enqueue_request(&state->queue, req);
+	spin_unlock_bh(&state->lock);
+
+	wake_up_process(state->task);
+	return err;
+}
+
+static void cryptd_hash_init(struct crypto_async_request *req_async, int err)
+{
+	struct cryptd_hash_ctx *ctx   = crypto_tfm_ctx(req_async->tfm);
+	struct crypto_hash     *child = ctx->child;
+	struct ahash_request    *req = ahash_request_cast(req_async);
+	struct cryptd_hash_request_ctx *rctx;
+	struct hash_desc desc;
+
+	rctx = ahash_request_ctx(req);
+
+	if (unlikely(err == -EINPROGRESS))
+		goto out;
+
+	desc.tfm = child;
+	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	err = crypto_hash_crt(child)->init(&desc);
+
+	req->base.complete = rctx->complete;
+
+out:
+	local_bh_disable();
+	rctx->complete(&req->base, err);
+	local_bh_enable();
+}
+
+static int cryptd_hash_init_enqueue(struct ahash_request *req)
+{
+	return cryptd_hash_enqueue(req, cryptd_hash_init);
+}
+
+static void cryptd_hash_update(struct crypto_async_request *req_async, int err)
+{
+	struct cryptd_hash_ctx *ctx   = crypto_tfm_ctx(req_async->tfm);
+	struct crypto_hash     *child = ctx->child;
+	struct ahash_request    *req = ahash_request_cast(req_async);
+	struct cryptd_hash_request_ctx *rctx;
+	struct hash_desc desc;
+
+	rctx = ahash_request_ctx(req);
+
+	if (unlikely(err == -EINPROGRESS))
+		goto out;
+
+	desc.tfm = child;
+	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	err = crypto_hash_crt(child)->update(&desc,
+						req->src,
+						req->nbytes);
+
+	req->base.complete = rctx->complete;
+
+out:
+	local_bh_disable();
+	rctx->complete(&req->base, err);
+	local_bh_enable();
+}
+
+static int cryptd_hash_update_enqueue(struct ahash_request *req)
+{
+	return cryptd_hash_enqueue(req, cryptd_hash_update);
+}
+
+static void cryptd_hash_final(struct crypto_async_request *req_async, int err)
+{
+	struct cryptd_hash_ctx *ctx   = crypto_tfm_ctx(req_async->tfm);
+	struct crypto_hash     *child = ctx->child;
+	struct ahash_request    *req = ahash_request_cast(req_async);
+	struct cryptd_hash_request_ctx *rctx;
+	struct hash_desc desc;
+
+	rctx = ahash_request_ctx(req);
+
+	if (unlikely(err == -EINPROGRESS))
+		goto out;
+
+	desc.tfm = child;
+	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	err = crypto_hash_crt(child)->final(&desc, req->result);
+
+	req->base.complete = rctx->complete;
+
+out:
+	local_bh_disable();
+	rctx->complete(&req->base, err);
+	local_bh_enable();
+}
+
+static int cryptd_hash_final_enqueue(struct ahash_request *req)
+{
+	return cryptd_hash_enqueue(req, cryptd_hash_final);
+}
+
+static void cryptd_hash_digest(struct crypto_async_request *req_async, int err)
+{
+	struct cryptd_hash_ctx *ctx   = crypto_tfm_ctx(req_async->tfm);
+	struct crypto_hash     *child = ctx->child;
+	struct ahash_request    *req = ahash_request_cast(req_async);
+	struct cryptd_hash_request_ctx *rctx;
+	struct hash_desc desc;
+
+	rctx = ahash_request_ctx(req);
+
+	if (unlikely(err == -EINPROGRESS))
+		goto out;
+
+	desc.tfm = child;
+	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	err = crypto_hash_crt(child)->digest(&desc,
+						req->src,
+						req->nbytes,
+						req->result);
+
+	req->base.complete = rctx->complete;
+
+out:
+	local_bh_disable();
+	rctx->complete(&req->base, err);
+	local_bh_enable();
+}
+
+static int cryptd_hash_digest_enqueue(struct ahash_request *req)
+{
+	return cryptd_hash_enqueue(req, cryptd_hash_digest);
+}
+
+static struct crypto_instance *cryptd_alloc_hash(
+	struct rtattr **tb, struct cryptd_state *state)
+{
+	struct crypto_instance *inst;
+	struct crypto_alg *alg;
+
+	alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_HASH,
+				  CRYPTO_ALG_TYPE_HASH_MASK);
+	if (IS_ERR(alg))
+		return ERR_PTR(PTR_ERR(alg));
+
+	inst = cryptd_alloc_instance(alg, state);
+	if (IS_ERR(inst))
+		goto out_put_alg;
+
+	inst->alg.cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC;
+	inst->alg.cra_type = &crypto_ahash_type;
+
+	inst->alg.cra_ahash.digestsize = alg->cra_hash.digestsize;
+	inst->alg.cra_ctxsize = sizeof(struct cryptd_hash_ctx);
+
+	inst->alg.cra_init = cryptd_hash_init_tfm;
+	inst->alg.cra_exit = cryptd_hash_exit_tfm;
+
+	inst->alg.cra_ahash.init   = cryptd_hash_init_enqueue;
+	inst->alg.cra_ahash.update = cryptd_hash_update_enqueue;
+	inst->alg.cra_ahash.final  = cryptd_hash_final_enqueue;
+	inst->alg.cra_ahash.setkey = cryptd_hash_setkey;
+	inst->alg.cra_ahash.digest = cryptd_hash_digest_enqueue;
+
+out_put_alg:
+	crypto_mod_put(alg);
+	return inst;
+}
+
 static struct cryptd_state state;
 
 static struct crypto_instance *cryptd_alloc(struct rtattr **tb)
@@ -274,6 +515,8 @@
 	switch (algt->type & algt->mask & CRYPTO_ALG_TYPE_MASK) {
 	case CRYPTO_ALG_TYPE_BLKCIPHER:
 		return cryptd_alloc_blkcipher(tb, &state);
+	case CRYPTO_ALG_TYPE_DIGEST:
+		return cryptd_alloc_hash(tb, &state);
 	}
 
 	return ERR_PTR(-EINVAL);
diff --git a/crypto/digest.c b/crypto/digest.c
index b526cc3..ac09194 100644
--- a/crypto/digest.c
+++ b/crypto/digest.c
@@ -12,6 +12,7 @@
  *
  */
 
+#include <crypto/internal/hash.h>
 #include <crypto/scatterwalk.h>
 #include <linux/mm.h>
 #include <linux/errno.h>
@@ -141,7 +142,7 @@
 	struct hash_tfm *ops = &tfm->crt_hash;
 	struct digest_alg *dalg = &tfm->__crt_alg->cra_digest;
 
-	if (dalg->dia_digestsize > crypto_tfm_alg_blocksize(tfm))
+	if (dalg->dia_digestsize > PAGE_SIZE / 8)
 		return -EINVAL;
 	
 	ops->init	= init;
@@ -157,3 +158,83 @@
 void crypto_exit_digest_ops(struct crypto_tfm *tfm)
 {
 }
+
+static int digest_async_nosetkey(struct crypto_ahash *tfm_async, const u8 *key,
+			unsigned int keylen)
+{
+	crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK);
+	return -ENOSYS;
+}
+
+static int digest_async_setkey(struct crypto_ahash *tfm_async, const u8 *key,
+			unsigned int keylen)
+{
+	struct crypto_tfm    *tfm        = crypto_ahash_tfm(tfm_async);
+	struct digest_alg    *dalg       = &tfm->__crt_alg->cra_digest;
+
+	crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK);
+	return dalg->dia_setkey(tfm, key, keylen);
+}
+
+static int digest_async_init(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm  = req->base.tfm;
+	struct digest_alg *dalg = &tfm->__crt_alg->cra_digest;
+
+	dalg->dia_init(tfm);
+	return 0;
+}
+
+static int digest_async_update(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm = req->base.tfm;
+	struct hash_desc  desc = {
+		.tfm   = __crypto_hash_cast(tfm),
+		.flags = req->base.flags,
+	};
+
+	update(&desc, req->src, req->nbytes);
+	return 0;
+}
+
+static int digest_async_final(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm  = req->base.tfm;
+	struct hash_desc  desc = {
+		.tfm   = __crypto_hash_cast(tfm),
+		.flags = req->base.flags,
+	};
+
+	final(&desc, req->result);
+	return 0;
+}
+
+static int digest_async_digest(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm  = req->base.tfm;
+	struct hash_desc  desc = {
+		.tfm   = __crypto_hash_cast(tfm),
+		.flags = req->base.flags,
+	};
+
+	return digest(&desc, req->src, req->nbytes, req->result);
+}
+
+int crypto_init_digest_ops_async(struct crypto_tfm *tfm)
+{
+	struct ahash_tfm  *crt  = &tfm->crt_ahash;
+	struct digest_alg *dalg = &tfm->__crt_alg->cra_digest;
+
+	if (dalg->dia_digestsize > crypto_tfm_alg_blocksize(tfm))
+		return -EINVAL;
+
+	crt->init       = digest_async_init;
+	crt->update     = digest_async_update;
+	crt->final      = digest_async_final;
+	crt->digest     = digest_async_digest;
+	crt->setkey     = dalg->dia_setkey ? digest_async_setkey :
+						digest_async_nosetkey;
+	crt->digestsize = dalg->dia_digestsize;
+
+	return 0;
+}
diff --git a/crypto/hash.c b/crypto/hash.c
index 7dcff67..cb86b19 100644
--- a/crypto/hash.c
+++ b/crypto/hash.c
@@ -9,6 +9,7 @@
  * any later version.
  */
 
+#include <crypto/internal/hash.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -59,24 +60,107 @@
 	return alg->setkey(crt, key, keylen);
 }
 
-static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+static int hash_async_setkey(struct crypto_ahash *tfm_async, const u8 *key,
+			unsigned int keylen)
+{
+	struct crypto_tfm  *tfm      = crypto_ahash_tfm(tfm_async);
+	struct crypto_hash *tfm_hash = __crypto_hash_cast(tfm);
+	struct hash_alg    *alg      = &tfm->__crt_alg->cra_hash;
+
+	return alg->setkey(tfm_hash, key, keylen);
+}
+
+static int hash_async_init(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm = req->base.tfm;
+	struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+	struct hash_desc  desc = {
+		.tfm = __crypto_hash_cast(tfm),
+		.flags = req->base.flags,
+	};
+
+	return alg->init(&desc);
+}
+
+static int hash_async_update(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm = req->base.tfm;
+	struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+	struct hash_desc  desc = {
+		.tfm = __crypto_hash_cast(tfm),
+		.flags = req->base.flags,
+	};
+
+	return alg->update(&desc, req->src, req->nbytes);
+}
+
+static int hash_async_final(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm = req->base.tfm;
+	struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+	struct hash_desc  desc = {
+		.tfm = __crypto_hash_cast(tfm),
+		.flags = req->base.flags,
+	};
+
+	return alg->final(&desc, req->result);
+}
+
+static int hash_async_digest(struct ahash_request *req)
+{
+	struct crypto_tfm *tfm = req->base.tfm;
+	struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+	struct hash_desc  desc = {
+		.tfm = __crypto_hash_cast(tfm),
+		.flags = req->base.flags,
+	};
+
+	return alg->digest(&desc, req->src, req->nbytes, req->result);
+}
+
+static int crypto_init_hash_ops_async(struct crypto_tfm *tfm)
+{
+	struct ahash_tfm *crt = &tfm->crt_ahash;
+	struct hash_alg  *alg = &tfm->__crt_alg->cra_hash;
+
+	crt->init       = hash_async_init;
+	crt->update     = hash_async_update;
+	crt->final      = hash_async_final;
+	crt->digest     = hash_async_digest;
+	crt->setkey     = hash_async_setkey;
+	crt->digestsize = alg->digestsize;
+
+	return 0;
+}
+
+static int crypto_init_hash_ops_sync(struct crypto_tfm *tfm)
 {
 	struct hash_tfm *crt = &tfm->crt_hash;
 	struct hash_alg *alg = &tfm->__crt_alg->cra_hash;
 
-	if (alg->digestsize > crypto_tfm_alg_blocksize(tfm))
-		return -EINVAL;
-
-	crt->init = alg->init;
-	crt->update = alg->update;
-	crt->final = alg->final;
-	crt->digest = alg->digest;
-	crt->setkey = hash_setkey;
+	crt->init       = alg->init;
+	crt->update     = alg->update;
+	crt->final      = alg->final;
+	crt->digest     = alg->digest;
+	crt->setkey     = hash_setkey;
 	crt->digestsize = alg->digestsize;
 
 	return 0;
 }
 
+static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+	struct hash_alg *alg = &tfm->__crt_alg->cra_hash;
+
+	if (alg->digestsize > PAGE_SIZE / 8)
+		return -EINVAL;
+
+	if ((mask & CRYPTO_ALG_TYPE_HASH_MASK) != CRYPTO_ALG_TYPE_HASH_MASK)
+		return crypto_init_hash_ops_async(tfm);
+	else
+		return crypto_init_hash_ops_sync(tfm);
+}
+
 static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg)
 	__attribute__ ((unused));
 static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg)
diff --git a/crypto/hmac.c b/crypto/hmac.c
index 14c6351..7ff2d6a 100644
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -226,6 +226,7 @@
 	struct crypto_instance *inst;
 	struct crypto_alg *alg;
 	int err;
+	int ds;
 
 	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_HASH);
 	if (err)
@@ -236,6 +237,13 @@
 	if (IS_ERR(alg))
 		return ERR_CAST(alg);
 
+	inst = ERR_PTR(-EINVAL);
+	ds = (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
+	     CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize :
+				    alg->cra_digest.dia_digestsize;
+	if (ds > alg->cra_blocksize)
+		goto out_put_alg;
+
 	inst = crypto_alloc_instance("hmac", alg);
 	if (IS_ERR(inst))
 		goto out_put_alg;
@@ -246,14 +254,10 @@
 	inst->alg.cra_alignmask = alg->cra_alignmask;
 	inst->alg.cra_type = &crypto_hash_type;
 
-	inst->alg.cra_hash.digestsize =
-		(alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
-		CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize :
-				       alg->cra_digest.dia_digestsize;
+	inst->alg.cra_hash.digestsize = ds;
 
 	inst->alg.cra_ctxsize = sizeof(struct hmac_ctx) +
-				ALIGN(inst->alg.cra_blocksize * 2 +
-				      inst->alg.cra_hash.digestsize,
+				ALIGN(inst->alg.cra_blocksize * 2 + ds,
 				      sizeof(void *));
 
 	inst->alg.cra_init = hmac_init_tfm;
diff --git a/crypto/internal.h b/crypto/internal.h
index 32f4c21..683fcb2 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -86,6 +86,7 @@
 struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask);
 
 int crypto_init_digest_ops(struct crypto_tfm *tfm);
+int crypto_init_digest_ops_async(struct crypto_tfm *tfm);
 int crypto_init_cipher_ops(struct crypto_tfm *tfm);
 int crypto_init_compress_ops(struct crypto_tfm *tfm);
 
diff --git a/crypto/prng.c b/crypto/prng.c
new file mode 100644
index 0000000..24e4f32
--- /dev/null
+++ b/crypto/prng.c
@@ -0,0 +1,410 @@
+/*
+ * PRNG: Pseudo Random Number Generator
+ *       Based on NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4 using
+ *       AES 128 cipher in RFC3686 ctr mode
+ *
+ *  (C) Neil Horman <nhorman@tuxdriver.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
+ *  any later version.
+ *
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/scatterlist.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/highmem.h>
+#include <linux/moduleparam.h>
+#include <linux/jiffies.h>
+#include <linux/timex.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include "prng.h"
+
+#define TEST_PRNG_ON_START 0
+
+#define DEFAULT_PRNG_KEY "0123456789abcdef1011"
+#define DEFAULT_PRNG_KSZ 20
+#define DEFAULT_PRNG_IV "defaultv"
+#define DEFAULT_PRNG_IVSZ 8
+#define DEFAULT_BLK_SZ 16
+#define DEFAULT_V_SEED "zaybxcwdveuftgsh"
+
+/*
+ * Flags for the prng_context flags field
+ */
+
+#define PRNG_FIXED_SIZE 0x1
+#define PRNG_NEED_RESET 0x2
+
+/*
+ * Note: DT is our counter value
+ * 	 I is our intermediate value
+ *	 V is our seed vector
+ * See http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
+ * for implementation details
+ */
+
+
+struct prng_context {
+	char *prng_key;
+	char *prng_iv;
+	spinlock_t prng_lock;
+	unsigned char rand_data[DEFAULT_BLK_SZ];
+	unsigned char last_rand_data[DEFAULT_BLK_SZ];
+	unsigned char DT[DEFAULT_BLK_SZ];
+	unsigned char I[DEFAULT_BLK_SZ];
+	unsigned char V[DEFAULT_BLK_SZ];
+	u32 rand_data_valid;
+	struct crypto_blkcipher *tfm;
+	u32 flags;
+};
+
+static int dbg;
+
+static void hexdump(char *note, unsigned char *buf, unsigned int len)
+{
+	if (dbg) {
+		printk(KERN_CRIT "%s", note);
+		print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,
+				16, 1,
+				buf, len, false);
+	}
+}
+
+#define dbgprint(format, args...) do {if(dbg) printk(format, ##args);} while(0)
+
+static void xor_vectors(unsigned char *in1, unsigned char *in2,
+		        unsigned char *out, unsigned int size)
+{
+	int i;
+
+	for (i=0;i<size;i++)
+		out[i] = in1[i] ^ in2[i];
+
+}
+/*
+ * Returns DEFAULT_BLK_SZ bytes of random data per call
+ * returns 0 if generation succeded, <0 if something went wrong
+ */
+static int _get_more_prng_bytes(struct prng_context *ctx)
+{
+	int i;
+	struct blkcipher_desc desc;
+	struct scatterlist sg_in, sg_out;
+	int ret;
+	unsigned char tmp[DEFAULT_BLK_SZ];
+
+	desc.tfm = ctx->tfm;
+	desc.flags = 0;
+
+
+	dbgprint(KERN_CRIT "Calling _get_more_prng_bytes for context %p\n",ctx);
+
+	hexdump("Input DT: ", ctx->DT, DEFAULT_BLK_SZ);
+	hexdump("Input I: ", ctx->I, DEFAULT_BLK_SZ);
+	hexdump("Input V: ", ctx->V, DEFAULT_BLK_SZ);
+
+	/*
+	 * This algorithm is a 3 stage state machine
+	 */
+	for (i=0;i<3;i++) {
+
+		desc.tfm = ctx->tfm;
+		desc.flags = 0;
+		switch (i) {
+			case 0:
+				/*
+				 * Start by encrypting the counter value
+				 * This gives us an intermediate value I
+				 */
+				memcpy(tmp, ctx->DT, DEFAULT_BLK_SZ);
+				sg_init_one(&sg_out, &ctx->I[0], DEFAULT_BLK_SZ);
+				hexdump("tmp stage 0: ", tmp, DEFAULT_BLK_SZ);
+				break;
+			case 1:
+
+				/*
+				 * Next xor I with our secret vector V
+				 * encrypt that result to obtain our
+				 * pseudo random data which we output
+				 */
+				xor_vectors(ctx->I, ctx->V, tmp, DEFAULT_BLK_SZ);
+				sg_init_one(&sg_out, &ctx->rand_data[0], DEFAULT_BLK_SZ);
+				hexdump("tmp stage 1: ", tmp, DEFAULT_BLK_SZ);
+				break;
+			case 2:
+				/*
+				 * First check that we didn't produce the same random data
+				 * that we did last time around through this
+				 */
+				if (!memcmp(ctx->rand_data, ctx->last_rand_data, DEFAULT_BLK_SZ)) {
+					printk(KERN_ERR "ctx %p Failed repetition check!\n",
+						ctx);
+					ctx->flags |= PRNG_NEED_RESET;
+					return -1;
+				}
+				memcpy(ctx->last_rand_data, ctx->rand_data, DEFAULT_BLK_SZ);
+
+				/*
+				 * Lastly xor the random data with I
+				 * and encrypt that to obtain a new secret vector V
+				 */
+				xor_vectors(ctx->rand_data, ctx->I, tmp, DEFAULT_BLK_SZ);
+				sg_init_one(&sg_out, &ctx->V[0], DEFAULT_BLK_SZ);
+				hexdump("tmp stage 2: ", tmp, DEFAULT_BLK_SZ);
+				break;
+		}
+
+		/* Initialize our input buffer */
+		sg_init_one(&sg_in, &tmp[0], DEFAULT_BLK_SZ);
+
+		/* do the encryption */
+		ret = crypto_blkcipher_encrypt(&desc, &sg_out, &sg_in, DEFAULT_BLK_SZ);
+
+		/* And check the result */
+		if (ret) {
+			dbgprint(KERN_CRIT "Encryption of new block failed for context %p\n",ctx);
+			ctx->rand_data_valid = DEFAULT_BLK_SZ;
+			return -1;
+		}
+
+	}
+
+	/*
+	 * Now update our DT value
+	 */
+	for (i=DEFAULT_BLK_SZ-1;i>0;i--) {
+		ctx->DT[i] = ctx->DT[i-1];
+	}
+	ctx->DT[0] += 1;
+
+	dbgprint("Returning new block for context %p\n",ctx);
+	ctx->rand_data_valid = 0;
+
+	hexdump("Output DT: ", ctx->DT, DEFAULT_BLK_SZ);
+	hexdump("Output I: ", ctx->I, DEFAULT_BLK_SZ);
+	hexdump("Output V: ", ctx->V, DEFAULT_BLK_SZ);
+	hexdump("New Random Data: ", ctx->rand_data, DEFAULT_BLK_SZ);
+
+	return 0;
+}
+
+/* Our exported functions */
+int get_prng_bytes(char *buf, int nbytes, struct prng_context *ctx)
+{
+	unsigned long flags;
+	unsigned char *ptr = buf;
+	unsigned int byte_count = (unsigned int)nbytes;
+	int err;
+
+
+	if (nbytes < 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ctx->prng_lock, flags);
+
+	err = -EFAULT;
+	if (ctx->flags & PRNG_NEED_RESET)
+		goto done;
+
+	/*
+	 * If the FIXED_SIZE flag is on, only return whole blocks of
+	 * pseudo random data
+	 */
+	err = -EINVAL;
+	if (ctx->flags & PRNG_FIXED_SIZE) {
+		if (nbytes < DEFAULT_BLK_SZ)
+			goto done;
+		byte_count = DEFAULT_BLK_SZ;
+	}
+
+	err = byte_count;
+
+	dbgprint(KERN_CRIT "getting %d random bytes for context %p\n",byte_count, ctx);
+
+
+remainder:
+	if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
+		if (_get_more_prng_bytes(ctx) < 0) {
+			memset(buf, 0, nbytes);
+			err = -EFAULT;
+			goto done;
+		}
+	}
+
+	/*
+	 * Copy up to the next whole block size
+	 */
+	if (byte_count < DEFAULT_BLK_SZ) {
+		for (;ctx->rand_data_valid < DEFAULT_BLK_SZ; ctx->rand_data_valid++) {
+			*ptr = ctx->rand_data[ctx->rand_data_valid];
+			ptr++;
+			byte_count--;
+			if (byte_count == 0)
+				goto done;
+		}
+	}
+
+	/*
+	 * Now copy whole blocks
+	 */
+	for(;byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) {
+		if (_get_more_prng_bytes(ctx) < 0) {
+			memset(buf, 0, nbytes);
+			err = -1;
+			goto done;
+		}
+		memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
+		ctx->rand_data_valid += DEFAULT_BLK_SZ;
+		ptr += DEFAULT_BLK_SZ;
+	}
+
+	/*
+	 * Now copy any extra partial data
+	 */
+	if (byte_count)
+		goto remainder;
+
+done:
+	spin_unlock_irqrestore(&ctx->prng_lock, flags);
+	dbgprint(KERN_CRIT "returning %d from get_prng_bytes in context %p\n",err, ctx);
+	return err;
+}
+EXPORT_SYMBOL_GPL(get_prng_bytes);
+
+struct prng_context *alloc_prng_context(void)
+{
+	struct prng_context *ctx=kzalloc(sizeof(struct prng_context), GFP_KERNEL);
+
+	spin_lock_init(&ctx->prng_lock);
+
+	if (reset_prng_context(ctx, NULL, NULL, NULL, NULL)) {
+		kfree(ctx);
+		ctx = NULL;
+	}
+
+	dbgprint(KERN_CRIT "returning context %p\n",ctx);
+	return ctx;
+}
+
+EXPORT_SYMBOL_GPL(alloc_prng_context);
+
+void free_prng_context(struct prng_context *ctx)
+{
+	crypto_free_blkcipher(ctx->tfm);
+	kfree(ctx);
+}
+EXPORT_SYMBOL_GPL(free_prng_context);
+
+int reset_prng_context(struct prng_context *ctx,
+		       unsigned char *key, unsigned char *iv,
+		       unsigned char *V, unsigned char *DT)
+{
+	int ret;
+	int iv_len;
+	int rc = -EFAULT;
+
+	spin_lock(&ctx->prng_lock);
+	ctx->flags |= PRNG_NEED_RESET;
+
+	if (key)
+		memcpy(ctx->prng_key,key,strlen(ctx->prng_key));
+	else
+		ctx->prng_key = DEFAULT_PRNG_KEY;
+
+	if (iv)
+		memcpy(ctx->prng_iv,iv, strlen(ctx->prng_iv));
+	else
+		ctx->prng_iv = DEFAULT_PRNG_IV;
+
+	if (V)
+		memcpy(ctx->V,V,DEFAULT_BLK_SZ);
+	else
+		memcpy(ctx->V,DEFAULT_V_SEED,DEFAULT_BLK_SZ);
+
+	if (DT)
+		memcpy(ctx->DT, DT, DEFAULT_BLK_SZ);
+	else
+		memset(ctx->DT, 0, DEFAULT_BLK_SZ);
+
+	memset(ctx->rand_data,0,DEFAULT_BLK_SZ);
+	memset(ctx->last_rand_data,0,DEFAULT_BLK_SZ);
+
+	if (ctx->tfm)
+		crypto_free_blkcipher(ctx->tfm);
+
+	ctx->tfm = crypto_alloc_blkcipher("rfc3686(ctr(aes))",0,0);
+	if (!ctx->tfm) {
+		dbgprint(KERN_CRIT "Failed to alloc crypto tfm for context %p\n",ctx->tfm);
+		goto out;
+	}
+
+	ctx->rand_data_valid = DEFAULT_BLK_SZ;
+
+	ret = crypto_blkcipher_setkey(ctx->tfm, ctx->prng_key, strlen(ctx->prng_key));
+	if (ret) {
+		dbgprint(KERN_CRIT "PRNG: setkey() failed flags=%x\n",
+			crypto_blkcipher_get_flags(ctx->tfm));
+		crypto_free_blkcipher(ctx->tfm);
+		goto out;
+	}
+
+	iv_len = crypto_blkcipher_ivsize(ctx->tfm);
+	if (iv_len) {
+		crypto_blkcipher_set_iv(ctx->tfm, ctx->prng_iv, iv_len);
+	}
+	rc = 0;
+	ctx->flags &= ~PRNG_NEED_RESET;
+out:
+	spin_unlock(&ctx->prng_lock);
+
+	return rc;
+
+}
+EXPORT_SYMBOL_GPL(reset_prng_context);
+
+/* Module initalization */
+static int __init prng_mod_init(void)
+{
+
+#ifdef TEST_PRNG_ON_START
+	int i;
+	unsigned char tmpbuf[DEFAULT_BLK_SZ];
+
+	struct prng_context *ctx = alloc_prng_context();
+	if (ctx == NULL)
+		return -EFAULT;
+	for (i=0;i<16;i++) {
+		if (get_prng_bytes(tmpbuf, DEFAULT_BLK_SZ, ctx) < 0) {
+			free_prng_context(ctx);
+			return -EFAULT;
+		}
+	}
+	free_prng_context(ctx);
+#endif
+
+	return 0;
+}
+
+static void __exit prng_mod_fini(void)
+{
+	return;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Software Pseudo Random Number Generator");
+MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>");
+module_param(dbg, int, 0);
+MODULE_PARM_DESC(dbg, "Boolean to enable debugging (0/1 == off/on)");
+module_init(prng_mod_init);
+module_exit(prng_mod_fini);
diff --git a/crypto/prng.h b/crypto/prng.h
new file mode 100644
index 0000000..1ac9be5
--- /dev/null
+++ b/crypto/prng.h
@@ -0,0 +1,27 @@
+/*
+ * PRNG: Pseudo Random Number Generator
+ *
+ *  (C) Neil Horman <nhorman@tuxdriver.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
+ *  any later version.
+ *
+ *
+ */
+
+#ifndef _PRNG_H_
+#define _PRNG_H_
+struct prng_context;
+
+int get_prng_bytes(char *buf, int nbytes, struct prng_context *ctx);
+struct prng_context *alloc_prng_context(void);
+int reset_prng_context(struct prng_context *ctx,
+			unsigned char *key, unsigned char *iv,
+			unsigned char *V,
+			unsigned char *DT);
+void free_prng_context(struct prng_context *ctx);
+
+#endif
+
diff --git a/crypto/ripemd.h b/crypto/ripemd.h
new file mode 100644
index 0000000..c57a2d4
--- /dev/null
+++ b/crypto/ripemd.h
@@ -0,0 +1,43 @@
+/*
+ * Common values for RIPEMD algorithms
+ */
+
+#ifndef _CRYPTO_RMD_H
+#define _CRYPTO_RMD_H
+
+#define RMD128_DIGEST_SIZE      16
+#define RMD128_BLOCK_SIZE       64
+
+#define RMD160_DIGEST_SIZE      20
+#define RMD160_BLOCK_SIZE       64
+
+#define RMD256_DIGEST_SIZE      32
+#define RMD256_BLOCK_SIZE       64
+
+#define RMD320_DIGEST_SIZE      40
+#define RMD320_BLOCK_SIZE       64
+
+/* initial values  */
+#define RMD_H0  0x67452301UL
+#define RMD_H1  0xefcdab89UL
+#define RMD_H2  0x98badcfeUL
+#define RMD_H3  0x10325476UL
+#define RMD_H4  0xc3d2e1f0UL
+#define RMD_H5  0x76543210UL
+#define RMD_H6  0xfedcba98UL
+#define RMD_H7  0x89abcdefUL
+#define RMD_H8  0x01234567UL
+#define RMD_H9  0x3c2d1e0fUL
+
+/* constants */
+#define RMD_K1  0x00000000UL
+#define RMD_K2  0x5a827999UL
+#define RMD_K3  0x6ed9eba1UL
+#define RMD_K4  0x8f1bbcdcUL
+#define RMD_K5  0xa953fd4eUL
+#define RMD_K6  0x50a28be6UL
+#define RMD_K7  0x5c4dd124UL
+#define RMD_K8  0x6d703ef3UL
+#define RMD_K9  0x7a6d76e9UL
+
+#endif
diff --git a/crypto/rmd128.c b/crypto/rmd128.c
new file mode 100644
index 0000000..5de6fa2
--- /dev/null
+++ b/crypto/rmd128.c
@@ -0,0 +1,325 @@
+/*
+ * Cryptographic API.
+ *
+ * RIPEMD-128 - RACE Integrity Primitives Evaluation Message Digest.
+ *
+ * Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
+ *
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/crypto.h>
+#include <linux/cryptohash.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#include "ripemd.h"
+
+struct rmd128_ctx {
+	u64 byte_count;
+	u32 state[4];
+	__le32 buffer[16];
+};
+
+#define K1  RMD_K1
+#define K2  RMD_K2
+#define K3  RMD_K3
+#define K4  RMD_K4
+#define KK1 RMD_K6
+#define KK2 RMD_K7
+#define KK3 RMD_K8
+#define KK4 RMD_K1
+
+#define F1(x, y, z) (x ^ y ^ z)		/* XOR */
+#define F2(x, y, z) (z ^ (x & (y ^ z)))	/* x ? y : z */
+#define F3(x, y, z) ((x | ~y) ^ z)
+#define F4(x, y, z) (y ^ (z & (x ^ y)))	/* z ? x : y */
+
+#define ROUND(a, b, c, d, f, k, x, s)  { \
+	(a) += f((b), (c), (d)) + le32_to_cpup(&(x)) + (k);	\
+	(a) = rol32((a), (s)); \
+}
+
+static void rmd128_transform(u32 *state, const __le32 *in)
+{
+	u32 aa, bb, cc, dd, aaa, bbb, ccc, ddd;
+
+	/* Initialize left lane */
+	aa = state[0];
+	bb = state[1];
+	cc = state[2];
+	dd = state[3];
+
+	/* Initialize right lane */
+	aaa = state[0];
+	bbb = state[1];
+	ccc = state[2];
+	ddd = state[3];
+
+	/* round 1: left lane */
+	ROUND(aa, bb, cc, dd, F1, K1, in[0],  11);
+	ROUND(dd, aa, bb, cc, F1, K1, in[1],  14);
+	ROUND(cc, dd, aa, bb, F1, K1, in[2],  15);
+	ROUND(bb, cc, dd, aa, F1, K1, in[3],  12);
+	ROUND(aa, bb, cc, dd, F1, K1, in[4],   5);
+	ROUND(dd, aa, bb, cc, F1, K1, in[5],   8);
+	ROUND(cc, dd, aa, bb, F1, K1, in[6],   7);
+	ROUND(bb, cc, dd, aa, F1, K1, in[7],   9);
+	ROUND(aa, bb, cc, dd, F1, K1, in[8],  11);
+	ROUND(dd, aa, bb, cc, F1, K1, in[9],  13);
+	ROUND(cc, dd, aa, bb, F1, K1, in[10], 14);
+	ROUND(bb, cc, dd, aa, F1, K1, in[11], 15);
+	ROUND(aa, bb, cc, dd, F1, K1, in[12],  6);
+	ROUND(dd, aa, bb, cc, F1, K1, in[13],  7);
+	ROUND(cc, dd, aa, bb, F1, K1, in[14],  9);
+	ROUND(bb, cc, dd, aa, F1, K1, in[15],  8);
+
+	/* round 2: left lane */
+	ROUND(aa, bb, cc, dd, F2, K2, in[7],   7);
+	ROUND(dd, aa, bb, cc, F2, K2, in[4],   6);
+	ROUND(cc, dd, aa, bb, F2, K2, in[13],  8);
+	ROUND(bb, cc, dd, aa, F2, K2, in[1],  13);
+	ROUND(aa, bb, cc, dd, F2, K2, in[10], 11);
+	ROUND(dd, aa, bb, cc, F2, K2, in[6],   9);
+	ROUND(cc, dd, aa, bb, F2, K2, in[15],  7);
+	ROUND(bb, cc, dd, aa, F2, K2, in[3],  15);
+	ROUND(aa, bb, cc, dd, F2, K2, in[12],  7);
+	ROUND(dd, aa, bb, cc, F2, K2, in[0],  12);
+	ROUND(cc, dd, aa, bb, F2, K2, in[9],  15);
+	ROUND(bb, cc, dd, aa, F2, K2, in[5],   9);
+	ROUND(aa, bb, cc, dd, F2, K2, in[2],  11);
+	ROUND(dd, aa, bb, cc, F2, K2, in[14],  7);
+	ROUND(cc, dd, aa, bb, F2, K2, in[11], 13);
+	ROUND(bb, cc, dd, aa, F2, K2, in[8],  12);
+
+	/* round 3: left lane */
+	ROUND(aa, bb, cc, dd, F3, K3, in[3],  11);
+	ROUND(dd, aa, bb, cc, F3, K3, in[10], 13);
+	ROUND(cc, dd, aa, bb, F3, K3, in[14],  6);
+	ROUND(bb, cc, dd, aa, F3, K3, in[4],   7);
+	ROUND(aa, bb, cc, dd, F3, K3, in[9],  14);
+	ROUND(dd, aa, bb, cc, F3, K3, in[15],  9);
+	ROUND(cc, dd, aa, bb, F3, K3, in[8],  13);
+	ROUND(bb, cc, dd, aa, F3, K3, in[1],  15);
+	ROUND(aa, bb, cc, dd, F3, K3, in[2],  14);
+	ROUND(dd, aa, bb, cc, F3, K3, in[7],   8);
+	ROUND(cc, dd, aa, bb, F3, K3, in[0],  13);
+	ROUND(bb, cc, dd, aa, F3, K3, in[6],   6);
+	ROUND(aa, bb, cc, dd, F3, K3, in[13],  5);
+	ROUND(dd, aa, bb, cc, F3, K3, in[11], 12);
+	ROUND(cc, dd, aa, bb, F3, K3, in[5],   7);
+	ROUND(bb, cc, dd, aa, F3, K3, in[12],  5);
+
+	/* round 4: left lane */
+	ROUND(aa, bb, cc, dd, F4, K4, in[1],  11);
+	ROUND(dd, aa, bb, cc, F4, K4, in[9],  12);
+	ROUND(cc, dd, aa, bb, F4, K4, in[11], 14);
+	ROUND(bb, cc, dd, aa, F4, K4, in[10], 15);
+	ROUND(aa, bb, cc, dd, F4, K4, in[0],  14);
+	ROUND(dd, aa, bb, cc, F4, K4, in[8],  15);
+	ROUND(cc, dd, aa, bb, F4, K4, in[12],  9);
+	ROUND(bb, cc, dd, aa, F4, K4, in[4],   8);
+	ROUND(aa, bb, cc, dd, F4, K4, in[13],  9);
+	ROUND(dd, aa, bb, cc, F4, K4, in[3],  14);
+	ROUND(cc, dd, aa, bb, F4, K4, in[7],   5);
+	ROUND(bb, cc, dd, aa, F4, K4, in[15],  6);
+	ROUND(aa, bb, cc, dd, F4, K4, in[14],  8);
+	ROUND(dd, aa, bb, cc, F4, K4, in[5],   6);
+	ROUND(cc, dd, aa, bb, F4, K4, in[6],   5);
+	ROUND(bb, cc, dd, aa, F4, K4, in[2],  12);
+
+	/* round 1: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[5],   8);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[14],  9);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[7],   9);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[0],  11);
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[9],  13);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[2],  15);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[11], 15);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[4],   5);
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[13],  7);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[6],   7);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[15],  8);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[8],  11);
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[1],  14);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[10], 14);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[3],  12);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[12],  6);
+
+	/* round 2: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[6],   9);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[11], 13);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[3],  15);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[7],   7);
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[0],  12);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[13],  8);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[5],   9);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[10], 11);
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[14],  7);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[15],  7);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[8],  12);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[12],  7);
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[4],   6);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[9],  15);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[1],  13);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[2],  11);
+
+	/* round 3: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[15],  9);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[5],   7);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[1],  15);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[3],  11);
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[7],   8);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[14],  6);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[6],   6);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[9],  14);
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[11], 12);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[8],  13);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[12],  5);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[2],  14);
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[10], 13);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[0],  13);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[4],   7);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[13],  5);
+
+	/* round 4: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[8],  15);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[6],   5);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[4],   8);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[1],  11);
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[3],  14);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[11], 14);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[15],  6);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[0],  14);
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[5],   6);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[12],  9);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[2],  12);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[13],  9);
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[9],  12);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[7],   5);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[10], 15);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[14],  8);
+
+	/* combine results */
+	ddd += cc + state[1];		/* final result for state[0] */
+	state[1] = state[2] + dd + aaa;
+	state[2] = state[3] + aa + bbb;
+	state[3] = state[0] + bb + ccc;
+	state[0] = ddd;
+
+	return;
+}
+
+static void rmd128_init(struct crypto_tfm *tfm)
+{
+	struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+
+	rctx->byte_count = 0;
+
+	rctx->state[0] = RMD_H0;
+	rctx->state[1] = RMD_H1;
+	rctx->state[2] = RMD_H2;
+	rctx->state[3] = RMD_H3;
+
+	memset(rctx->buffer, 0, sizeof(rctx->buffer));
+}
+
+static void rmd128_update(struct crypto_tfm *tfm, const u8 *data,
+			  unsigned int len)
+{
+	struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+	const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
+
+	rctx->byte_count += len;
+
+	/* Enough space in buffer? If so copy and we're done */
+	if (avail > len) {
+		memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+		       data, len);
+		return;
+	}
+
+	memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+	       data, avail);
+
+	rmd128_transform(rctx->state, rctx->buffer);
+	data += avail;
+	len -= avail;
+
+	while (len >= sizeof(rctx->buffer)) {
+		memcpy(rctx->buffer, data, sizeof(rctx->buffer));
+		rmd128_transform(rctx->state, rctx->buffer);
+		data += sizeof(rctx->buffer);
+		len -= sizeof(rctx->buffer);
+	}
+
+	memcpy(rctx->buffer, data, len);
+}
+
+/* Add padding and return the message digest. */
+static void rmd128_final(struct crypto_tfm *tfm, u8 *out)
+{
+	struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+	u32 i, index, padlen;
+	__le64 bits;
+	__le32 *dst = (__le32 *)out;
+	static const u8 padding[64] = { 0x80, };
+
+	bits = cpu_to_le64(rctx->byte_count << 3);
+
+	/* Pad out to 56 mod 64 */
+	index = rctx->byte_count & 0x3f;
+	padlen = (index < 56) ? (56 - index) : ((64+56) - index);
+	rmd128_update(tfm, padding, padlen);
+
+	/* Append length */
+	rmd128_update(tfm, (const u8 *)&bits, sizeof(bits));
+
+	/* Store state in digest */
+	for (i = 0; i < 4; i++)
+		dst[i] = cpu_to_le32p(&rctx->state[i]);
+
+	/* Wipe context */
+	memset(rctx, 0, sizeof(*rctx));
+}
+
+static struct crypto_alg alg = {
+	.cra_name	 =	"rmd128",
+	.cra_driver_name =	"rmd128",
+	.cra_flags	 =	CRYPTO_ALG_TYPE_DIGEST,
+	.cra_blocksize	 =	RMD128_BLOCK_SIZE,
+	.cra_ctxsize	 =	sizeof(struct rmd128_ctx),
+	.cra_module	 =	THIS_MODULE,
+	.cra_list	 =	LIST_HEAD_INIT(alg.cra_list),
+	.cra_u		 =	{ .digest = {
+	.dia_digestsize	 =	RMD128_DIGEST_SIZE,
+	.dia_init	 =	rmd128_init,
+	.dia_update	 =	rmd128_update,
+	.dia_final	 =	rmd128_final } }
+};
+
+static int __init rmd128_mod_init(void)
+{
+	return crypto_register_alg(&alg);
+}
+
+static void __exit rmd128_mod_fini(void)
+{
+	crypto_unregister_alg(&alg);
+}
+
+module_init(rmd128_mod_init);
+module_exit(rmd128_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RIPEMD-128 Message Digest");
+
+MODULE_ALIAS("rmd128");
diff --git a/crypto/rmd160.c b/crypto/rmd160.c
new file mode 100644
index 0000000..f001ec7
--- /dev/null
+++ b/crypto/rmd160.c
@@ -0,0 +1,369 @@
+/*
+ * Cryptographic API.
+ *
+ * RIPEMD-160 - RACE Integrity Primitives Evaluation Message Digest.
+ *
+ * Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
+ *
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/crypto.h>
+#include <linux/cryptohash.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#include "ripemd.h"
+
+struct rmd160_ctx {
+	u64 byte_count;
+	u32 state[5];
+	__le32 buffer[16];
+};
+
+#define K1  RMD_K1
+#define K2  RMD_K2
+#define K3  RMD_K3
+#define K4  RMD_K4
+#define K5  RMD_K5
+#define KK1 RMD_K6
+#define KK2 RMD_K7
+#define KK3 RMD_K8
+#define KK4 RMD_K9
+#define KK5 RMD_K1
+
+#define F1(x, y, z) (x ^ y ^ z)		/* XOR */
+#define F2(x, y, z) (z ^ (x & (y ^ z)))	/* x ? y : z */
+#define F3(x, y, z) ((x | ~y) ^ z)
+#define F4(x, y, z) (y ^ (z & (x ^ y)))	/* z ? x : y */
+#define F5(x, y, z) (x ^ (y | ~z))
+
+#define ROUND(a, b, c, d, e, f, k, x, s)  { \
+	(a) += f((b), (c), (d)) + le32_to_cpup(&(x)) + (k); \
+	(a) = rol32((a), (s)) + (e); \
+	(c) = rol32((c), 10); \
+}
+
+static void rmd160_transform(u32 *state, const __le32 *in)
+{
+	u32 aa, bb, cc, dd, ee, aaa, bbb, ccc, ddd, eee;
+
+	/* Initialize left lane */
+	aa = state[0];
+	bb = state[1];
+	cc = state[2];
+	dd = state[3];
+	ee = state[4];
+
+	/* Initialize right lane */
+	aaa = state[0];
+	bbb = state[1];
+	ccc = state[2];
+	ddd = state[3];
+	eee = state[4];
+
+	/* round 1: left lane */
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[0],  11);
+	ROUND(ee, aa, bb, cc, dd, F1, K1, in[1],  14);
+	ROUND(dd, ee, aa, bb, cc, F1, K1, in[2],  15);
+	ROUND(cc, dd, ee, aa, bb, F1, K1, in[3],  12);
+	ROUND(bb, cc, dd, ee, aa, F1, K1, in[4],   5);
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[5],   8);
+	ROUND(ee, aa, bb, cc, dd, F1, K1, in[6],   7);
+	ROUND(dd, ee, aa, bb, cc, F1, K1, in[7],   9);
+	ROUND(cc, dd, ee, aa, bb, F1, K1, in[8],  11);
+	ROUND(bb, cc, dd, ee, aa, F1, K1, in[9],  13);
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[10], 14);
+	ROUND(ee, aa, bb, cc, dd, F1, K1, in[11], 15);
+	ROUND(dd, ee, aa, bb, cc, F1, K1, in[12],  6);
+	ROUND(cc, dd, ee, aa, bb, F1, K1, in[13],  7);
+	ROUND(bb, cc, dd, ee, aa, F1, K1, in[14],  9);
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[15],  8);
+
+	/* round 2: left lane" */
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[7],   7);
+	ROUND(dd, ee, aa, bb, cc, F2, K2, in[4],   6);
+	ROUND(cc, dd, ee, aa, bb, F2, K2, in[13],  8);
+	ROUND(bb, cc, dd, ee, aa, F2, K2, in[1],  13);
+	ROUND(aa, bb, cc, dd, ee, F2, K2, in[10], 11);
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[6],   9);
+	ROUND(dd, ee, aa, bb, cc, F2, K2, in[15],  7);
+	ROUND(cc, dd, ee, aa, bb, F2, K2, in[3],  15);
+	ROUND(bb, cc, dd, ee, aa, F2, K2, in[12],  7);
+	ROUND(aa, bb, cc, dd, ee, F2, K2, in[0],  12);
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[9],  15);
+	ROUND(dd, ee, aa, bb, cc, F2, K2, in[5],   9);
+	ROUND(cc, dd, ee, aa, bb, F2, K2, in[2],  11);
+	ROUND(bb, cc, dd, ee, aa, F2, K2, in[14],  7);
+	ROUND(aa, bb, cc, dd, ee, F2, K2, in[11], 13);
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[8],  12);
+
+	/* round 3: left lane" */
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[3],  11);
+	ROUND(cc, dd, ee, aa, bb, F3, K3, in[10], 13);
+	ROUND(bb, cc, dd, ee, aa, F3, K3, in[14],  6);
+	ROUND(aa, bb, cc, dd, ee, F3, K3, in[4],   7);
+	ROUND(ee, aa, bb, cc, dd, F3, K3, in[9],  14);
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[15],  9);
+	ROUND(cc, dd, ee, aa, bb, F3, K3, in[8],  13);
+	ROUND(bb, cc, dd, ee, aa, F3, K3, in[1],  15);
+	ROUND(aa, bb, cc, dd, ee, F3, K3, in[2],  14);
+	ROUND(ee, aa, bb, cc, dd, F3, K3, in[7],   8);
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[0],  13);
+	ROUND(cc, dd, ee, aa, bb, F3, K3, in[6],   6);
+	ROUND(bb, cc, dd, ee, aa, F3, K3, in[13],  5);
+	ROUND(aa, bb, cc, dd, ee, F3, K3, in[11], 12);
+	ROUND(ee, aa, bb, cc, dd, F3, K3, in[5],   7);
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[12],  5);
+
+	/* round 4: left lane" */
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[1],  11);
+	ROUND(bb, cc, dd, ee, aa, F4, K4, in[9],  12);
+	ROUND(aa, bb, cc, dd, ee, F4, K4, in[11], 14);
+	ROUND(ee, aa, bb, cc, dd, F4, K4, in[10], 15);
+	ROUND(dd, ee, aa, bb, cc, F4, K4, in[0],  14);
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[8],  15);
+	ROUND(bb, cc, dd, ee, aa, F4, K4, in[12],  9);
+	ROUND(aa, bb, cc, dd, ee, F4, K4, in[4],   8);
+	ROUND(ee, aa, bb, cc, dd, F4, K4, in[13],  9);
+	ROUND(dd, ee, aa, bb, cc, F4, K4, in[3],  14);
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[7],   5);
+	ROUND(bb, cc, dd, ee, aa, F4, K4, in[15],  6);
+	ROUND(aa, bb, cc, dd, ee, F4, K4, in[14],  8);
+	ROUND(ee, aa, bb, cc, dd, F4, K4, in[5],   6);
+	ROUND(dd, ee, aa, bb, cc, F4, K4, in[6],   5);
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[2],  12);
+
+	/* round 5: left lane" */
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[4],   9);
+	ROUND(aa, bb, cc, dd, ee, F5, K5, in[0],  15);
+	ROUND(ee, aa, bb, cc, dd, F5, K5, in[5],   5);
+	ROUND(dd, ee, aa, bb, cc, F5, K5, in[9],  11);
+	ROUND(cc, dd, ee, aa, bb, F5, K5, in[7],   6);
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[12],  8);
+	ROUND(aa, bb, cc, dd, ee, F5, K5, in[2],  13);
+	ROUND(ee, aa, bb, cc, dd, F5, K5, in[10], 12);
+	ROUND(dd, ee, aa, bb, cc, F5, K5, in[14],  5);
+	ROUND(cc, dd, ee, aa, bb, F5, K5, in[1],  12);
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[3],  13);
+	ROUND(aa, bb, cc, dd, ee, F5, K5, in[8],  14);
+	ROUND(ee, aa, bb, cc, dd, F5, K5, in[11], 11);
+	ROUND(dd, ee, aa, bb, cc, F5, K5, in[6],   8);
+	ROUND(cc, dd, ee, aa, bb, F5, K5, in[15],  5);
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[13],  6);
+
+	/* round 1: right lane */
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[5],   8);
+	ROUND(eee, aaa, bbb, ccc, ddd, F5, KK1, in[14],  9);
+	ROUND(ddd, eee, aaa, bbb, ccc, F5, KK1, in[7],   9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F5, KK1, in[0],  11);
+	ROUND(bbb, ccc, ddd, eee, aaa, F5, KK1, in[9],  13);
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[2],  15);
+	ROUND(eee, aaa, bbb, ccc, ddd, F5, KK1, in[11], 15);
+	ROUND(ddd, eee, aaa, bbb, ccc, F5, KK1, in[4],   5);
+	ROUND(ccc, ddd, eee, aaa, bbb, F5, KK1, in[13],  7);
+	ROUND(bbb, ccc, ddd, eee, aaa, F5, KK1, in[6],   7);
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[15],  8);
+	ROUND(eee, aaa, bbb, ccc, ddd, F5, KK1, in[8],  11);
+	ROUND(ddd, eee, aaa, bbb, ccc, F5, KK1, in[1],  14);
+	ROUND(ccc, ddd, eee, aaa, bbb, F5, KK1, in[10], 14);
+	ROUND(bbb, ccc, ddd, eee, aaa, F5, KK1, in[3],  12);
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[12],  6);
+
+	/* round 2: right lane */
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[6],   9);
+	ROUND(ddd, eee, aaa, bbb, ccc, F4, KK2, in[11], 13);
+	ROUND(ccc, ddd, eee, aaa, bbb, F4, KK2, in[3],  15);
+	ROUND(bbb, ccc, ddd, eee, aaa, F4, KK2, in[7],   7);
+	ROUND(aaa, bbb, ccc, ddd, eee, F4, KK2, in[0],  12);
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[13],  8);
+	ROUND(ddd, eee, aaa, bbb, ccc, F4, KK2, in[5],   9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F4, KK2, in[10], 11);
+	ROUND(bbb, ccc, ddd, eee, aaa, F4, KK2, in[14],  7);
+	ROUND(aaa, bbb, ccc, ddd, eee, F4, KK2, in[15],  7);
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[8],  12);
+	ROUND(ddd, eee, aaa, bbb, ccc, F4, KK2, in[12],  7);
+	ROUND(ccc, ddd, eee, aaa, bbb, F4, KK2, in[4],   6);
+	ROUND(bbb, ccc, ddd, eee, aaa, F4, KK2, in[9],  15);
+	ROUND(aaa, bbb, ccc, ddd, eee, F4, KK2, in[1],  13);
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[2],  11);
+
+	/* round 3: right lane */
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[15],  9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F3, KK3, in[5],   7);
+	ROUND(bbb, ccc, ddd, eee, aaa, F3, KK3, in[1],  15);
+	ROUND(aaa, bbb, ccc, ddd, eee, F3, KK3, in[3],  11);
+	ROUND(eee, aaa, bbb, ccc, ddd, F3, KK3, in[7],   8);
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[14],  6);
+	ROUND(ccc, ddd, eee, aaa, bbb, F3, KK3, in[6],   6);
+	ROUND(bbb, ccc, ddd, eee, aaa, F3, KK3, in[9],  14);
+	ROUND(aaa, bbb, ccc, ddd, eee, F3, KK3, in[11], 12);
+	ROUND(eee, aaa, bbb, ccc, ddd, F3, KK3, in[8],  13);
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[12],  5);
+	ROUND(ccc, ddd, eee, aaa, bbb, F3, KK3, in[2],  14);
+	ROUND(bbb, ccc, ddd, eee, aaa, F3, KK3, in[10], 13);
+	ROUND(aaa, bbb, ccc, ddd, eee, F3, KK3, in[0],  13);
+	ROUND(eee, aaa, bbb, ccc, ddd, F3, KK3, in[4],   7);
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[13],  5);
+
+	/* round 4: right lane */
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[8],  15);
+	ROUND(bbb, ccc, ddd, eee, aaa, F2, KK4, in[6],   5);
+	ROUND(aaa, bbb, ccc, ddd, eee, F2, KK4, in[4],   8);
+	ROUND(eee, aaa, bbb, ccc, ddd, F2, KK4, in[1],  11);
+	ROUND(ddd, eee, aaa, bbb, ccc, F2, KK4, in[3],  14);
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[11], 14);
+	ROUND(bbb, ccc, ddd, eee, aaa, F2, KK4, in[15],  6);
+	ROUND(aaa, bbb, ccc, ddd, eee, F2, KK4, in[0],  14);
+	ROUND(eee, aaa, bbb, ccc, ddd, F2, KK4, in[5],   6);
+	ROUND(ddd, eee, aaa, bbb, ccc, F2, KK4, in[12],  9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[2],  12);
+	ROUND(bbb, ccc, ddd, eee, aaa, F2, KK4, in[13],  9);
+	ROUND(aaa, bbb, ccc, ddd, eee, F2, KK4, in[9],  12);
+	ROUND(eee, aaa, bbb, ccc, ddd, F2, KK4, in[7],   5);
+	ROUND(ddd, eee, aaa, bbb, ccc, F2, KK4, in[10], 15);
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[14],  8);
+
+	/* round 5: right lane */
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[12],  8);
+	ROUND(aaa, bbb, ccc, ddd, eee, F1, KK5, in[15],  5);
+	ROUND(eee, aaa, bbb, ccc, ddd, F1, KK5, in[10], 12);
+	ROUND(ddd, eee, aaa, bbb, ccc, F1, KK5, in[4],   9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F1, KK5, in[1],  12);
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[5],   5);
+	ROUND(aaa, bbb, ccc, ddd, eee, F1, KK5, in[8],  14);
+	ROUND(eee, aaa, bbb, ccc, ddd, F1, KK5, in[7],   6);
+	ROUND(ddd, eee, aaa, bbb, ccc, F1, KK5, in[6],   8);
+	ROUND(ccc, ddd, eee, aaa, bbb, F1, KK5, in[2],  13);
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[13],  6);
+	ROUND(aaa, bbb, ccc, ddd, eee, F1, KK5, in[14],  5);
+	ROUND(eee, aaa, bbb, ccc, ddd, F1, KK5, in[0],  15);
+	ROUND(ddd, eee, aaa, bbb, ccc, F1, KK5, in[3],  13);
+	ROUND(ccc, ddd, eee, aaa, bbb, F1, KK5, in[9],  11);
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[11], 11);
+
+	/* combine results */
+	ddd += cc + state[1];		/* final result for state[0] */
+	state[1] = state[2] + dd + eee;
+	state[2] = state[3] + ee + aaa;
+	state[3] = state[4] + aa + bbb;
+	state[4] = state[0] + bb + ccc;
+	state[0] = ddd;
+
+	return;
+}
+
+static void rmd160_init(struct crypto_tfm *tfm)
+{
+	struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+
+	rctx->byte_count = 0;
+
+	rctx->state[0] = RMD_H0;
+	rctx->state[1] = RMD_H1;
+	rctx->state[2] = RMD_H2;
+	rctx->state[3] = RMD_H3;
+	rctx->state[4] = RMD_H4;
+
+	memset(rctx->buffer, 0, sizeof(rctx->buffer));
+}
+
+static void rmd160_update(struct crypto_tfm *tfm, const u8 *data,
+			  unsigned int len)
+{
+	struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+	const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
+
+	rctx->byte_count += len;
+
+	/* Enough space in buffer? If so copy and we're done */
+	if (avail > len) {
+		memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+		       data, len);
+		return;
+	}
+
+	memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+	       data, avail);
+
+	rmd160_transform(rctx->state, rctx->buffer);
+	data += avail;
+	len -= avail;
+
+	while (len >= sizeof(rctx->buffer)) {
+		memcpy(rctx->buffer, data, sizeof(rctx->buffer));
+		rmd160_transform(rctx->state, rctx->buffer);
+		data += sizeof(rctx->buffer);
+		len -= sizeof(rctx->buffer);
+	}
+
+	memcpy(rctx->buffer, data, len);
+}
+
+/* Add padding and return the message digest. */
+static void rmd160_final(struct crypto_tfm *tfm, u8 *out)
+{
+	struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+	u32 i, index, padlen;
+	__le64 bits;
+	__le32 *dst = (__le32 *)out;
+	static const u8 padding[64] = { 0x80, };
+
+	bits = cpu_to_le64(rctx->byte_count << 3);
+
+	/* Pad out to 56 mod 64 */
+	index = rctx->byte_count & 0x3f;
+	padlen = (index < 56) ? (56 - index) : ((64+56) - index);
+	rmd160_update(tfm, padding, padlen);
+
+	/* Append length */
+	rmd160_update(tfm, (const u8 *)&bits, sizeof(bits));
+
+	/* Store state in digest */
+	for (i = 0; i < 5; i++)
+		dst[i] = cpu_to_le32p(&rctx->state[i]);
+
+	/* Wipe context */
+	memset(rctx, 0, sizeof(*rctx));
+}
+
+static struct crypto_alg alg = {
+	.cra_name	 =	"rmd160",
+	.cra_driver_name =	"rmd160",
+	.cra_flags	 =	CRYPTO_ALG_TYPE_DIGEST,
+	.cra_blocksize	 =	RMD160_BLOCK_SIZE,
+	.cra_ctxsize	 =	sizeof(struct rmd160_ctx),
+	.cra_module	 =	THIS_MODULE,
+	.cra_list	 =	LIST_HEAD_INIT(alg.cra_list),
+	.cra_u		 =	{ .digest = {
+	.dia_digestsize	 =	RMD160_DIGEST_SIZE,
+	.dia_init	 =	rmd160_init,
+	.dia_update	 =	rmd160_update,
+	.dia_final	 =	rmd160_final } }
+};
+
+static int __init rmd160_mod_init(void)
+{
+	return crypto_register_alg(&alg);
+}
+
+static void __exit rmd160_mod_fini(void)
+{
+	crypto_unregister_alg(&alg);
+}
+
+module_init(rmd160_mod_init);
+module_exit(rmd160_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RIPEMD-160 Message Digest");
+
+MODULE_ALIAS("rmd160");
diff --git a/crypto/rmd256.c b/crypto/rmd256.c
new file mode 100644
index 0000000..e3de5b4
--- /dev/null
+++ b/crypto/rmd256.c
@@ -0,0 +1,344 @@
+/*
+ * Cryptographic API.
+ *
+ * RIPEMD-256 - RACE Integrity Primitives Evaluation Message Digest.
+ *
+ * Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
+ *
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/crypto.h>
+#include <linux/cryptohash.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#include "ripemd.h"
+
+struct rmd256_ctx {
+	u64 byte_count;
+	u32 state[8];
+	__le32 buffer[16];
+};
+
+#define K1  RMD_K1
+#define K2  RMD_K2
+#define K3  RMD_K3
+#define K4  RMD_K4
+#define KK1 RMD_K6
+#define KK2 RMD_K7
+#define KK3 RMD_K8
+#define KK4 RMD_K1
+
+#define F1(x, y, z) (x ^ y ^ z)		/* XOR */
+#define F2(x, y, z) (z ^ (x & (y ^ z)))	/* x ? y : z */
+#define F3(x, y, z) ((x | ~y) ^ z)
+#define F4(x, y, z) (y ^ (z & (x ^ y)))	/* z ? x : y */
+
+#define ROUND(a, b, c, d, f, k, x, s)  { \
+	(a) += f((b), (c), (d)) + le32_to_cpup(&(x)) + (k); \
+	(a) = rol32((a), (s)); \
+}
+
+static void rmd256_transform(u32 *state, const __le32 *in)
+{
+	u32 aa, bb, cc, dd, aaa, bbb, ccc, ddd, tmp;
+
+	/* Initialize left lane */
+	aa = state[0];
+	bb = state[1];
+	cc = state[2];
+	dd = state[3];
+
+	/* Initialize right lane */
+	aaa = state[4];
+	bbb = state[5];
+	ccc = state[6];
+	ddd = state[7];
+
+	/* round 1: left lane */
+	ROUND(aa, bb, cc, dd, F1, K1, in[0],  11);
+	ROUND(dd, aa, bb, cc, F1, K1, in[1],  14);
+	ROUND(cc, dd, aa, bb, F1, K1, in[2],  15);
+	ROUND(bb, cc, dd, aa, F1, K1, in[3],  12);
+	ROUND(aa, bb, cc, dd, F1, K1, in[4],   5);
+	ROUND(dd, aa, bb, cc, F1, K1, in[5],   8);
+	ROUND(cc, dd, aa, bb, F1, K1, in[6],   7);
+	ROUND(bb, cc, dd, aa, F1, K1, in[7],   9);
+	ROUND(aa, bb, cc, dd, F1, K1, in[8],  11);
+	ROUND(dd, aa, bb, cc, F1, K1, in[9],  13);
+	ROUND(cc, dd, aa, bb, F1, K1, in[10], 14);
+	ROUND(bb, cc, dd, aa, F1, K1, in[11], 15);
+	ROUND(aa, bb, cc, dd, F1, K1, in[12],  6);
+	ROUND(dd, aa, bb, cc, F1, K1, in[13],  7);
+	ROUND(cc, dd, aa, bb, F1, K1, in[14],  9);
+	ROUND(bb, cc, dd, aa, F1, K1, in[15],  8);
+
+	/* round 1: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[5],   8);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[14],  9);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[7],   9);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[0],  11);
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[9],  13);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[2],  15);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[11], 15);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[4],   5);
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[13],  7);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[6],   7);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[15],  8);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[8],  11);
+	ROUND(aaa, bbb, ccc, ddd, F4, KK1, in[1],  14);
+	ROUND(ddd, aaa, bbb, ccc, F4, KK1, in[10], 14);
+	ROUND(ccc, ddd, aaa, bbb, F4, KK1, in[3],  12);
+	ROUND(bbb, ccc, ddd, aaa, F4, KK1, in[12],  6);
+
+	/* Swap contents of "a" registers */
+	tmp = aa; aa = aaa; aaa = tmp;
+
+	/* round 2: left lane */
+	ROUND(aa, bb, cc, dd, F2, K2, in[7],   7);
+	ROUND(dd, aa, bb, cc, F2, K2, in[4],   6);
+	ROUND(cc, dd, aa, bb, F2, K2, in[13],  8);
+	ROUND(bb, cc, dd, aa, F2, K2, in[1],  13);
+	ROUND(aa, bb, cc, dd, F2, K2, in[10], 11);
+	ROUND(dd, aa, bb, cc, F2, K2, in[6],   9);
+	ROUND(cc, dd, aa, bb, F2, K2, in[15],  7);
+	ROUND(bb, cc, dd, aa, F2, K2, in[3],  15);
+	ROUND(aa, bb, cc, dd, F2, K2, in[12],  7);
+	ROUND(dd, aa, bb, cc, F2, K2, in[0],  12);
+	ROUND(cc, dd, aa, bb, F2, K2, in[9],  15);
+	ROUND(bb, cc, dd, aa, F2, K2, in[5],   9);
+	ROUND(aa, bb, cc, dd, F2, K2, in[2],  11);
+	ROUND(dd, aa, bb, cc, F2, K2, in[14],  7);
+	ROUND(cc, dd, aa, bb, F2, K2, in[11], 13);
+	ROUND(bb, cc, dd, aa, F2, K2, in[8],  12);
+
+	/* round 2: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[6],   9);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[11], 13);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[3],  15);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[7],   7);
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[0],  12);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[13],  8);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[5],   9);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[10], 11);
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[14],  7);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[15],  7);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[8],  12);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[12],  7);
+	ROUND(aaa, bbb, ccc, ddd, F3, KK2, in[4],   6);
+	ROUND(ddd, aaa, bbb, ccc, F3, KK2, in[9],  15);
+	ROUND(ccc, ddd, aaa, bbb, F3, KK2, in[1],  13);
+	ROUND(bbb, ccc, ddd, aaa, F3, KK2, in[2],  11);
+
+	/* Swap contents of "b" registers */
+	tmp = bb; bb = bbb; bbb = tmp;
+
+	/* round 3: left lane */
+	ROUND(aa, bb, cc, dd, F3, K3, in[3],  11);
+	ROUND(dd, aa, bb, cc, F3, K3, in[10], 13);
+	ROUND(cc, dd, aa, bb, F3, K3, in[14],  6);
+	ROUND(bb, cc, dd, aa, F3, K3, in[4],   7);
+	ROUND(aa, bb, cc, dd, F3, K3, in[9],  14);
+	ROUND(dd, aa, bb, cc, F3, K3, in[15],  9);
+	ROUND(cc, dd, aa, bb, F3, K3, in[8],  13);
+	ROUND(bb, cc, dd, aa, F3, K3, in[1],  15);
+	ROUND(aa, bb, cc, dd, F3, K3, in[2],  14);
+	ROUND(dd, aa, bb, cc, F3, K3, in[7],   8);
+	ROUND(cc, dd, aa, bb, F3, K3, in[0],  13);
+	ROUND(bb, cc, dd, aa, F3, K3, in[6],   6);
+	ROUND(aa, bb, cc, dd, F3, K3, in[13],  5);
+	ROUND(dd, aa, bb, cc, F3, K3, in[11], 12);
+	ROUND(cc, dd, aa, bb, F3, K3, in[5],   7);
+	ROUND(bb, cc, dd, aa, F3, K3, in[12],  5);
+
+	/* round 3: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[15],  9);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[5],   7);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[1],  15);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[3],  11);
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[7],   8);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[14],  6);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[6],   6);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[9],  14);
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[11], 12);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[8],  13);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[12],  5);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[2],  14);
+	ROUND(aaa, bbb, ccc, ddd, F2, KK3, in[10], 13);
+	ROUND(ddd, aaa, bbb, ccc, F2, KK3, in[0],  13);
+	ROUND(ccc, ddd, aaa, bbb, F2, KK3, in[4],   7);
+	ROUND(bbb, ccc, ddd, aaa, F2, KK3, in[13],  5);
+
+	/* Swap contents of "c" registers */
+	tmp = cc; cc = ccc; ccc = tmp;
+
+	/* round 4: left lane */
+	ROUND(aa, bb, cc, dd, F4, K4, in[1],  11);
+	ROUND(dd, aa, bb, cc, F4, K4, in[9],  12);
+	ROUND(cc, dd, aa, bb, F4, K4, in[11], 14);
+	ROUND(bb, cc, dd, aa, F4, K4, in[10], 15);
+	ROUND(aa, bb, cc, dd, F4, K4, in[0],  14);
+	ROUND(dd, aa, bb, cc, F4, K4, in[8],  15);
+	ROUND(cc, dd, aa, bb, F4, K4, in[12],  9);
+	ROUND(bb, cc, dd, aa, F4, K4, in[4],   8);
+	ROUND(aa, bb, cc, dd, F4, K4, in[13],  9);
+	ROUND(dd, aa, bb, cc, F4, K4, in[3],  14);
+	ROUND(cc, dd, aa, bb, F4, K4, in[7],   5);
+	ROUND(bb, cc, dd, aa, F4, K4, in[15],  6);
+	ROUND(aa, bb, cc, dd, F4, K4, in[14],  8);
+	ROUND(dd, aa, bb, cc, F4, K4, in[5],   6);
+	ROUND(cc, dd, aa, bb, F4, K4, in[6],   5);
+	ROUND(bb, cc, dd, aa, F4, K4, in[2],  12);
+
+	/* round 4: right lane */
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[8],  15);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[6],   5);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[4],   8);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[1],  11);
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[3],  14);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[11], 14);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[15],  6);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[0],  14);
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[5],   6);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[12],  9);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[2],  12);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[13],  9);
+	ROUND(aaa, bbb, ccc, ddd, F1, KK4, in[9],  12);
+	ROUND(ddd, aaa, bbb, ccc, F1, KK4, in[7],   5);
+	ROUND(ccc, ddd, aaa, bbb, F1, KK4, in[10], 15);
+	ROUND(bbb, ccc, ddd, aaa, F1, KK4, in[14],  8);
+
+	/* Swap contents of "d" registers */
+	tmp = dd; dd = ddd; ddd = tmp;
+
+	/* combine results */
+	state[0] += aa;
+	state[1] += bb;
+	state[2] += cc;
+	state[3] += dd;
+	state[4] += aaa;
+	state[5] += bbb;
+	state[6] += ccc;
+	state[7] += ddd;
+
+	return;
+}
+
+static void rmd256_init(struct crypto_tfm *tfm)
+{
+	struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+
+	rctx->byte_count = 0;
+
+	rctx->state[0] = RMD_H0;
+	rctx->state[1] = RMD_H1;
+	rctx->state[2] = RMD_H2;
+	rctx->state[3] = RMD_H3;
+	rctx->state[4] = RMD_H5;
+	rctx->state[5] = RMD_H6;
+	rctx->state[6] = RMD_H7;
+	rctx->state[7] = RMD_H8;
+
+	memset(rctx->buffer, 0, sizeof(rctx->buffer));
+}
+
+static void rmd256_update(struct crypto_tfm *tfm, const u8 *data,
+			  unsigned int len)
+{
+	struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+	const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
+
+	rctx->byte_count += len;
+
+	/* Enough space in buffer? If so copy and we're done */
+	if (avail > len) {
+		memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+		       data, len);
+		return;
+	}
+
+	memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+	       data, avail);
+
+	rmd256_transform(rctx->state, rctx->buffer);
+	data += avail;
+	len -= avail;
+
+	while (len >= sizeof(rctx->buffer)) {
+		memcpy(rctx->buffer, data, sizeof(rctx->buffer));
+		rmd256_transform(rctx->state, rctx->buffer);
+		data += sizeof(rctx->buffer);
+		len -= sizeof(rctx->buffer);
+	}
+
+	memcpy(rctx->buffer, data, len);
+}
+
+/* Add padding and return the message digest. */
+static void rmd256_final(struct crypto_tfm *tfm, u8 *out)
+{
+	struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+	u32 i, index, padlen;
+	__le64 bits;
+	__le32 *dst = (__le32 *)out;
+	static const u8 padding[64] = { 0x80, };
+
+	bits = cpu_to_le64(rctx->byte_count << 3);
+
+	/* Pad out to 56 mod 64 */
+	index = rctx->byte_count & 0x3f;
+	padlen = (index < 56) ? (56 - index) : ((64+56) - index);
+	rmd256_update(tfm, padding, padlen);
+
+	/* Append length */
+	rmd256_update(tfm, (const u8 *)&bits, sizeof(bits));
+
+	/* Store state in digest */
+	for (i = 0; i < 8; i++)
+		dst[i] = cpu_to_le32p(&rctx->state[i]);
+
+	/* Wipe context */
+	memset(rctx, 0, sizeof(*rctx));
+}
+
+static struct crypto_alg alg = {
+	.cra_name	 =	"rmd256",
+	.cra_driver_name =	"rmd256",
+	.cra_flags	 =	CRYPTO_ALG_TYPE_DIGEST,
+	.cra_blocksize	 =	RMD256_BLOCK_SIZE,
+	.cra_ctxsize	 =	sizeof(struct rmd256_ctx),
+	.cra_module	 =	THIS_MODULE,
+	.cra_list	 =	LIST_HEAD_INIT(alg.cra_list),
+	.cra_u		 =	{ .digest = {
+	.dia_digestsize	 =	RMD256_DIGEST_SIZE,
+	.dia_init	 =	rmd256_init,
+	.dia_update	 =	rmd256_update,
+	.dia_final	 =	rmd256_final } }
+};
+
+static int __init rmd256_mod_init(void)
+{
+	return crypto_register_alg(&alg);
+}
+
+static void __exit rmd256_mod_fini(void)
+{
+	crypto_unregister_alg(&alg);
+}
+
+module_init(rmd256_mod_init);
+module_exit(rmd256_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RIPEMD-256 Message Digest");
+
+MODULE_ALIAS("rmd256");
diff --git a/crypto/rmd320.c b/crypto/rmd320.c
new file mode 100644
index 0000000..b143d66
--- /dev/null
+++ b/crypto/rmd320.c
@@ -0,0 +1,393 @@
+/*
+ * Cryptographic API.
+ *
+ * RIPEMD-320 - RACE Integrity Primitives Evaluation Message Digest.
+ *
+ * Based on the reference implementation by Antoon Bosselaers, ESAT-COSIC
+ *
+ * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger (at) swiss-it.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/crypto.h>
+#include <linux/cryptohash.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#include "ripemd.h"
+
+struct rmd320_ctx {
+	u64 byte_count;
+	u32 state[10];
+	__le32 buffer[16];
+};
+
+#define K1  RMD_K1
+#define K2  RMD_K2
+#define K3  RMD_K3
+#define K4  RMD_K4
+#define K5  RMD_K5
+#define KK1 RMD_K6
+#define KK2 RMD_K7
+#define KK3 RMD_K8
+#define KK4 RMD_K9
+#define KK5 RMD_K1
+
+#define F1(x, y, z) (x ^ y ^ z)		/* XOR */
+#define F2(x, y, z) (z ^ (x & (y ^ z)))	/* x ? y : z */
+#define F3(x, y, z) ((x | ~y) ^ z)
+#define F4(x, y, z) (y ^ (z & (x ^ y)))	/* z ? x : y */
+#define F5(x, y, z) (x ^ (y | ~z))
+
+#define ROUND(a, b, c, d, e, f, k, x, s)  { \
+	(a) += f((b), (c), (d)) + le32_to_cpup(&(x)) + (k); \
+	(a) = rol32((a), (s)) + (e); \
+	(c) = rol32((c), 10); \
+}
+
+static void rmd320_transform(u32 *state, const __le32 *in)
+{
+	u32 aa, bb, cc, dd, ee, aaa, bbb, ccc, ddd, eee, tmp;
+
+	/* Initialize left lane */
+	aa = state[0];
+	bb = state[1];
+	cc = state[2];
+	dd = state[3];
+	ee = state[4];
+
+	/* Initialize right lane */
+	aaa = state[5];
+	bbb = state[6];
+	ccc = state[7];
+	ddd = state[8];
+	eee = state[9];
+
+	/* round 1: left lane */
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[0],  11);
+	ROUND(ee, aa, bb, cc, dd, F1, K1, in[1],  14);
+	ROUND(dd, ee, aa, bb, cc, F1, K1, in[2],  15);
+	ROUND(cc, dd, ee, aa, bb, F1, K1, in[3],  12);
+	ROUND(bb, cc, dd, ee, aa, F1, K1, in[4],   5);
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[5],   8);
+	ROUND(ee, aa, bb, cc, dd, F1, K1, in[6],   7);
+	ROUND(dd, ee, aa, bb, cc, F1, K1, in[7],   9);
+	ROUND(cc, dd, ee, aa, bb, F1, K1, in[8],  11);
+	ROUND(bb, cc, dd, ee, aa, F1, K1, in[9],  13);
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[10], 14);
+	ROUND(ee, aa, bb, cc, dd, F1, K1, in[11], 15);
+	ROUND(dd, ee, aa, bb, cc, F1, K1, in[12],  6);
+	ROUND(cc, dd, ee, aa, bb, F1, K1, in[13],  7);
+	ROUND(bb, cc, dd, ee, aa, F1, K1, in[14],  9);
+	ROUND(aa, bb, cc, dd, ee, F1, K1, in[15],  8);
+
+	/* round 1: right lane */
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[5],   8);
+	ROUND(eee, aaa, bbb, ccc, ddd, F5, KK1, in[14],  9);
+	ROUND(ddd, eee, aaa, bbb, ccc, F5, KK1, in[7],   9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F5, KK1, in[0],  11);
+	ROUND(bbb, ccc, ddd, eee, aaa, F5, KK1, in[9],  13);
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[2],  15);
+	ROUND(eee, aaa, bbb, ccc, ddd, F5, KK1, in[11], 15);
+	ROUND(ddd, eee, aaa, bbb, ccc, F5, KK1, in[4],   5);
+	ROUND(ccc, ddd, eee, aaa, bbb, F5, KK1, in[13],  7);
+	ROUND(bbb, ccc, ddd, eee, aaa, F5, KK1, in[6],   7);
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[15],  8);
+	ROUND(eee, aaa, bbb, ccc, ddd, F5, KK1, in[8],  11);
+	ROUND(ddd, eee, aaa, bbb, ccc, F5, KK1, in[1],  14);
+	ROUND(ccc, ddd, eee, aaa, bbb, F5, KK1, in[10], 14);
+	ROUND(bbb, ccc, ddd, eee, aaa, F5, KK1, in[3],  12);
+	ROUND(aaa, bbb, ccc, ddd, eee, F5, KK1, in[12],  6);
+
+	/* Swap contents of "a" registers */
+	tmp = aa; aa = aaa; aaa = tmp;
+
+	/* round 2: left lane" */
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[7],   7);
+	ROUND(dd, ee, aa, bb, cc, F2, K2, in[4],   6);
+	ROUND(cc, dd, ee, aa, bb, F2, K2, in[13],  8);
+	ROUND(bb, cc, dd, ee, aa, F2, K2, in[1],  13);
+	ROUND(aa, bb, cc, dd, ee, F2, K2, in[10], 11);
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[6],   9);
+	ROUND(dd, ee, aa, bb, cc, F2, K2, in[15],  7);
+	ROUND(cc, dd, ee, aa, bb, F2, K2, in[3],  15);
+	ROUND(bb, cc, dd, ee, aa, F2, K2, in[12],  7);
+	ROUND(aa, bb, cc, dd, ee, F2, K2, in[0],  12);
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[9],  15);
+	ROUND(dd, ee, aa, bb, cc, F2, K2, in[5],   9);
+	ROUND(cc, dd, ee, aa, bb, F2, K2, in[2],  11);
+	ROUND(bb, cc, dd, ee, aa, F2, K2, in[14],  7);
+	ROUND(aa, bb, cc, dd, ee, F2, K2, in[11], 13);
+	ROUND(ee, aa, bb, cc, dd, F2, K2, in[8],  12);
+
+	/* round 2: right lane */
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[6],   9);
+	ROUND(ddd, eee, aaa, bbb, ccc, F4, KK2, in[11], 13);
+	ROUND(ccc, ddd, eee, aaa, bbb, F4, KK2, in[3],  15);
+	ROUND(bbb, ccc, ddd, eee, aaa, F4, KK2, in[7],   7);
+	ROUND(aaa, bbb, ccc, ddd, eee, F4, KK2, in[0],  12);
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[13],  8);
+	ROUND(ddd, eee, aaa, bbb, ccc, F4, KK2, in[5],   9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F4, KK2, in[10], 11);
+	ROUND(bbb, ccc, ddd, eee, aaa, F4, KK2, in[14],  7);
+	ROUND(aaa, bbb, ccc, ddd, eee, F4, KK2, in[15],  7);
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[8],  12);
+	ROUND(ddd, eee, aaa, bbb, ccc, F4, KK2, in[12],  7);
+	ROUND(ccc, ddd, eee, aaa, bbb, F4, KK2, in[4],   6);
+	ROUND(bbb, ccc, ddd, eee, aaa, F4, KK2, in[9],  15);
+	ROUND(aaa, bbb, ccc, ddd, eee, F4, KK2, in[1],  13);
+	ROUND(eee, aaa, bbb, ccc, ddd, F4, KK2, in[2],  11);
+
+	/* Swap contents of "b" registers */
+	tmp = bb; bb = bbb; bbb = tmp;
+
+	/* round 3: left lane" */
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[3],  11);
+	ROUND(cc, dd, ee, aa, bb, F3, K3, in[10], 13);
+	ROUND(bb, cc, dd, ee, aa, F3, K3, in[14],  6);
+	ROUND(aa, bb, cc, dd, ee, F3, K3, in[4],   7);
+	ROUND(ee, aa, bb, cc, dd, F3, K3, in[9],  14);
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[15],  9);
+	ROUND(cc, dd, ee, aa, bb, F3, K3, in[8],  13);
+	ROUND(bb, cc, dd, ee, aa, F3, K3, in[1],  15);
+	ROUND(aa, bb, cc, dd, ee, F3, K3, in[2],  14);
+	ROUND(ee, aa, bb, cc, dd, F3, K3, in[7],   8);
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[0],  13);
+	ROUND(cc, dd, ee, aa, bb, F3, K3, in[6],   6);
+	ROUND(bb, cc, dd, ee, aa, F3, K3, in[13],  5);
+	ROUND(aa, bb, cc, dd, ee, F3, K3, in[11], 12);
+	ROUND(ee, aa, bb, cc, dd, F3, K3, in[5],   7);
+	ROUND(dd, ee, aa, bb, cc, F3, K3, in[12],  5);
+
+	/* round 3: right lane */
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[15],  9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F3, KK3, in[5],   7);
+	ROUND(bbb, ccc, ddd, eee, aaa, F3, KK3, in[1],  15);
+	ROUND(aaa, bbb, ccc, ddd, eee, F3, KK3, in[3],  11);
+	ROUND(eee, aaa, bbb, ccc, ddd, F3, KK3, in[7],   8);
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[14],  6);
+	ROUND(ccc, ddd, eee, aaa, bbb, F3, KK3, in[6],   6);
+	ROUND(bbb, ccc, ddd, eee, aaa, F3, KK3, in[9],  14);
+	ROUND(aaa, bbb, ccc, ddd, eee, F3, KK3, in[11], 12);
+	ROUND(eee, aaa, bbb, ccc, ddd, F3, KK3, in[8],  13);
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[12],  5);
+	ROUND(ccc, ddd, eee, aaa, bbb, F3, KK3, in[2],  14);
+	ROUND(bbb, ccc, ddd, eee, aaa, F3, KK3, in[10], 13);
+	ROUND(aaa, bbb, ccc, ddd, eee, F3, KK3, in[0],  13);
+	ROUND(eee, aaa, bbb, ccc, ddd, F3, KK3, in[4],   7);
+	ROUND(ddd, eee, aaa, bbb, ccc, F3, KK3, in[13],  5);
+
+	/* Swap contents of "c" registers */
+	tmp = cc; cc = ccc; ccc = tmp;
+
+	/* round 4: left lane" */
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[1],  11);
+	ROUND(bb, cc, dd, ee, aa, F4, K4, in[9],  12);
+	ROUND(aa, bb, cc, dd, ee, F4, K4, in[11], 14);
+	ROUND(ee, aa, bb, cc, dd, F4, K4, in[10], 15);
+	ROUND(dd, ee, aa, bb, cc, F4, K4, in[0],  14);
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[8],  15);
+	ROUND(bb, cc, dd, ee, aa, F4, K4, in[12],  9);
+	ROUND(aa, bb, cc, dd, ee, F4, K4, in[4],   8);
+	ROUND(ee, aa, bb, cc, dd, F4, K4, in[13],  9);
+	ROUND(dd, ee, aa, bb, cc, F4, K4, in[3],  14);
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[7],   5);
+	ROUND(bb, cc, dd, ee, aa, F4, K4, in[15],  6);
+	ROUND(aa, bb, cc, dd, ee, F4, K4, in[14],  8);
+	ROUND(ee, aa, bb, cc, dd, F4, K4, in[5],   6);
+	ROUND(dd, ee, aa, bb, cc, F4, K4, in[6],   5);
+	ROUND(cc, dd, ee, aa, bb, F4, K4, in[2],  12);
+
+	/* round 4: right lane */
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[8],  15);
+	ROUND(bbb, ccc, ddd, eee, aaa, F2, KK4, in[6],   5);
+	ROUND(aaa, bbb, ccc, ddd, eee, F2, KK4, in[4],   8);
+	ROUND(eee, aaa, bbb, ccc, ddd, F2, KK4, in[1],  11);
+	ROUND(ddd, eee, aaa, bbb, ccc, F2, KK4, in[3],  14);
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[11], 14);
+	ROUND(bbb, ccc, ddd, eee, aaa, F2, KK4, in[15],  6);
+	ROUND(aaa, bbb, ccc, ddd, eee, F2, KK4, in[0],  14);
+	ROUND(eee, aaa, bbb, ccc, ddd, F2, KK4, in[5],   6);
+	ROUND(ddd, eee, aaa, bbb, ccc, F2, KK4, in[12],  9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[2],  12);
+	ROUND(bbb, ccc, ddd, eee, aaa, F2, KK4, in[13],  9);
+	ROUND(aaa, bbb, ccc, ddd, eee, F2, KK4, in[9],  12);
+	ROUND(eee, aaa, bbb, ccc, ddd, F2, KK4, in[7],   5);
+	ROUND(ddd, eee, aaa, bbb, ccc, F2, KK4, in[10], 15);
+	ROUND(ccc, ddd, eee, aaa, bbb, F2, KK4, in[14],  8);
+
+	/* Swap contents of "d" registers */
+	tmp = dd; dd = ddd; ddd = tmp;
+
+	/* round 5: left lane" */
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[4],   9);
+	ROUND(aa, bb, cc, dd, ee, F5, K5, in[0],  15);
+	ROUND(ee, aa, bb, cc, dd, F5, K5, in[5],   5);
+	ROUND(dd, ee, aa, bb, cc, F5, K5, in[9],  11);
+	ROUND(cc, dd, ee, aa, bb, F5, K5, in[7],   6);
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[12],  8);
+	ROUND(aa, bb, cc, dd, ee, F5, K5, in[2],  13);
+	ROUND(ee, aa, bb, cc, dd, F5, K5, in[10], 12);
+	ROUND(dd, ee, aa, bb, cc, F5, K5, in[14],  5);
+	ROUND(cc, dd, ee, aa, bb, F5, K5, in[1],  12);
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[3],  13);
+	ROUND(aa, bb, cc, dd, ee, F5, K5, in[8],  14);
+	ROUND(ee, aa, bb, cc, dd, F5, K5, in[11], 11);
+	ROUND(dd, ee, aa, bb, cc, F5, K5, in[6],   8);
+	ROUND(cc, dd, ee, aa, bb, F5, K5, in[15],  5);
+	ROUND(bb, cc, dd, ee, aa, F5, K5, in[13],  6);
+
+	/* round 5: right lane */
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[12],  8);
+	ROUND(aaa, bbb, ccc, ddd, eee, F1, KK5, in[15],  5);
+	ROUND(eee, aaa, bbb, ccc, ddd, F1, KK5, in[10], 12);
+	ROUND(ddd, eee, aaa, bbb, ccc, F1, KK5, in[4],   9);
+	ROUND(ccc, ddd, eee, aaa, bbb, F1, KK5, in[1],  12);
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[5],   5);
+	ROUND(aaa, bbb, ccc, ddd, eee, F1, KK5, in[8],  14);
+	ROUND(eee, aaa, bbb, ccc, ddd, F1, KK5, in[7],   6);
+	ROUND(ddd, eee, aaa, bbb, ccc, F1, KK5, in[6],   8);
+	ROUND(ccc, ddd, eee, aaa, bbb, F1, KK5, in[2],  13);
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[13],  6);
+	ROUND(aaa, bbb, ccc, ddd, eee, F1, KK5, in[14],  5);
+	ROUND(eee, aaa, bbb, ccc, ddd, F1, KK5, in[0],  15);
+	ROUND(ddd, eee, aaa, bbb, ccc, F1, KK5, in[3],  13);
+	ROUND(ccc, ddd, eee, aaa, bbb, F1, KK5, in[9],  11);
+	ROUND(bbb, ccc, ddd, eee, aaa, F1, KK5, in[11], 11);
+
+	/* Swap contents of "e" registers */
+	tmp = ee; ee = eee; eee = tmp;
+
+	/* combine results */
+	state[0] += aa;
+	state[1] += bb;
+	state[2] += cc;
+	state[3] += dd;
+	state[4] += ee;
+	state[5] += aaa;
+	state[6] += bbb;
+	state[7] += ccc;
+	state[8] += ddd;
+	state[9] += eee;
+
+	return;
+}
+
+static void rmd320_init(struct crypto_tfm *tfm)
+{
+	struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+
+	rctx->byte_count = 0;
+
+	rctx->state[0] = RMD_H0;
+	rctx->state[1] = RMD_H1;
+	rctx->state[2] = RMD_H2;
+	rctx->state[3] = RMD_H3;
+	rctx->state[4] = RMD_H4;
+	rctx->state[5] = RMD_H5;
+	rctx->state[6] = RMD_H6;
+	rctx->state[7] = RMD_H7;
+	rctx->state[8] = RMD_H8;
+	rctx->state[9] = RMD_H9;
+
+	memset(rctx->buffer, 0, sizeof(rctx->buffer));
+}
+
+static void rmd320_update(struct crypto_tfm *tfm, const u8 *data,
+			  unsigned int len)
+{
+	struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+	const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
+
+	rctx->byte_count += len;
+
+	/* Enough space in buffer? If so copy and we're done */
+	if (avail > len) {
+		memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+		       data, len);
+		return;
+	}
+
+	memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
+	       data, avail);
+
+	rmd320_transform(rctx->state, rctx->buffer);
+	data += avail;
+	len -= avail;
+
+	while (len >= sizeof(rctx->buffer)) {
+		memcpy(rctx->buffer, data, sizeof(rctx->buffer));
+		rmd320_transform(rctx->state, rctx->buffer);
+		data += sizeof(rctx->buffer);
+		len -= sizeof(rctx->buffer);
+	}
+
+	memcpy(rctx->buffer, data, len);
+}
+
+/* Add padding and return the message digest. */
+static void rmd320_final(struct crypto_tfm *tfm, u8 *out)
+{
+	struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+	u32 i, index, padlen;
+	__le64 bits;
+	__le32 *dst = (__le32 *)out;
+	static const u8 padding[64] = { 0x80, };
+
+	bits = cpu_to_le64(rctx->byte_count << 3);
+
+	/* Pad out to 56 mod 64 */
+	index = rctx->byte_count & 0x3f;
+	padlen = (index < 56) ? (56 - index) : ((64+56) - index);
+	rmd320_update(tfm, padding, padlen);
+
+	/* Append length */
+	rmd320_update(tfm, (const u8 *)&bits, sizeof(bits));
+
+	/* Store state in digest */
+	for (i = 0; i < 10; i++)
+		dst[i] = cpu_to_le32p(&rctx->state[i]);
+
+	/* Wipe context */
+	memset(rctx, 0, sizeof(*rctx));
+}
+
+static struct crypto_alg alg = {
+	.cra_name	 =	"rmd320",
+	.cra_driver_name =	"rmd320",
+	.cra_flags	 =	CRYPTO_ALG_TYPE_DIGEST,
+	.cra_blocksize	 =	RMD320_BLOCK_SIZE,
+	.cra_ctxsize	 =	sizeof(struct rmd320_ctx),
+	.cra_module	 =	THIS_MODULE,
+	.cra_list	 =	LIST_HEAD_INIT(alg.cra_list),
+	.cra_u		 =	{ .digest = {
+	.dia_digestsize	 =	RMD320_DIGEST_SIZE,
+	.dia_init	 =	rmd320_init,
+	.dia_update	 =	rmd320_update,
+	.dia_final	 =	rmd320_final } }
+};
+
+static int __init rmd320_mod_init(void)
+{
+	return crypto_register_alg(&alg);
+}
+
+static void __exit rmd320_mod_fini(void)
+{
+	crypto_unregister_alg(&alg);
+}
+
+module_init(rmd320_mod_init);
+module_exit(rmd320_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RIPEMD-320 Message Digest");
+
+MODULE_ALIAS("rmd320");
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index e47f6e0..59821a2 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -13,15 +13,9 @@
  * Software Foundation; either version 2 of the License, or (at your option)
  * any later version.
  *
- * 2007-11-13 Added GCM tests
- * 2007-11-13 Added AEAD support
- * 2007-11-06 Added SHA-224 and SHA-224-HMAC tests
- * 2006-12-07 Added SHA384 HMAC and SHA512 HMAC tests
- * 2004-08-09 Added cipher speed tests (Reyk Floeter <reyk@vantronix.net>)
- * 2003-09-14 Rewritten by Kartikey Mahendra Bhatt
- *
  */
 
+#include <crypto/hash.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -30,7 +24,6 @@
 #include <linux/scatterlist.h>
 #include <linux/string.h>
 #include <linux/crypto.h>
-#include <linux/highmem.h>
 #include <linux/moduleparam.h>
 #include <linux/jiffies.h>
 #include <linux/timex.h>
@@ -38,7 +31,7 @@
 #include "tcrypt.h"
 
 /*
- * Need to kmalloc() memory for testing kmap().
+ * Need to kmalloc() memory for testing.
  */
 #define TVMEMSIZE	16384
 #define XBUFSIZE	32768
@@ -46,7 +39,7 @@
 /*
  * Indexes into the xbuf to simulate cross-page access.
  */
-#define IDX1		37
+#define IDX1		32
 #define IDX2		32400
 #define IDX3		1
 #define IDX4		8193
@@ -83,7 +76,8 @@
 	"blowfish", "twofish", "serpent", "sha384", "sha512", "md4", "aes",
 	"cast6", "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea",
 	"khazad", "wp512", "wp384", "wp256", "tnepres", "xeta",  "fcrypt",
-	"camellia", "seed", "salsa20", "lzo", "cts", NULL
+	"camellia", "seed", "salsa20", "rmd128", "rmd160", "rmd256", "rmd320",
+	"lzo", "cts", NULL
 };
 
 static void hexdump(unsigned char *buf, unsigned int len)
@@ -110,22 +104,30 @@
 	unsigned int i, j, k, temp;
 	struct scatterlist sg[8];
 	char result[64];
-	struct crypto_hash *tfm;
-	struct hash_desc desc;
+	struct crypto_ahash *tfm;
+	struct ahash_request *req;
+	struct tcrypt_result tresult;
 	int ret;
 	void *hash_buff;
 
 	printk("\ntesting %s\n", algo);
 
-	tfm = crypto_alloc_hash(algo, 0, CRYPTO_ALG_ASYNC);
+	init_completion(&tresult.completion);
+
+	tfm = crypto_alloc_ahash(algo, 0, 0);
 	if (IS_ERR(tfm)) {
 		printk("failed to load transform for %s: %ld\n", algo,
 		       PTR_ERR(tfm));
 		return;
 	}
 
-	desc.tfm = tfm;
-	desc.flags = 0;
+	req = ahash_request_alloc(tfm, GFP_KERNEL);
+	if (!req) {
+		printk(KERN_ERR "failed to allocate request for %s\n", algo);
+		goto out_noreq;
+	}
+	ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+				   tcrypt_complete, &tresult);
 
 	for (i = 0; i < tcount; i++) {
 		printk("test %u:\n", i + 1);
@@ -139,8 +141,9 @@
 		sg_init_one(&sg[0], hash_buff, template[i].psize);
 
 		if (template[i].ksize) {
-			ret = crypto_hash_setkey(tfm, template[i].key,
-						 template[i].ksize);
+			crypto_ahash_clear_flags(tfm, ~0);
+			ret = crypto_ahash_setkey(tfm, template[i].key,
+						  template[i].ksize);
 			if (ret) {
 				printk("setkey() failed ret=%d\n", ret);
 				kfree(hash_buff);
@@ -148,17 +151,30 @@
 			}
 		}
 
-		ret = crypto_hash_digest(&desc, sg, template[i].psize, result);
-		if (ret) {
+		ahash_request_set_crypt(req, sg, result, template[i].psize);
+		ret = crypto_ahash_digest(req);
+		switch (ret) {
+		case 0:
+			break;
+		case -EINPROGRESS:
+		case -EBUSY:
+			ret = wait_for_completion_interruptible(
+				&tresult.completion);
+			if (!ret && !(ret = tresult.err)) {
+				INIT_COMPLETION(tresult.completion);
+				break;
+			}
+			/* fall through */
+		default:
 			printk("digest () failed ret=%d\n", ret);
 			kfree(hash_buff);
 			goto out;
 		}
 
-		hexdump(result, crypto_hash_digestsize(tfm));
+		hexdump(result, crypto_ahash_digestsize(tfm));
 		printk("%s\n",
 		       memcmp(result, template[i].digest,
-			      crypto_hash_digestsize(tfm)) ?
+			      crypto_ahash_digestsize(tfm)) ?
 		       "fail" : "pass");
 		kfree(hash_buff);
 	}
@@ -187,8 +203,9 @@
 			}
 
 			if (template[i].ksize) {
-				ret = crypto_hash_setkey(tfm, template[i].key,
-							 template[i].ksize);
+				crypto_ahash_clear_flags(tfm, ~0);
+				ret = crypto_ahash_setkey(tfm, template[i].key,
+							  template[i].ksize);
 
 				if (ret) {
 					printk("setkey() failed ret=%d\n", ret);
@@ -196,29 +213,44 @@
 				}
 			}
 
-			ret = crypto_hash_digest(&desc, sg, template[i].psize,
-						 result);
-			if (ret) {
+			ahash_request_set_crypt(req, sg, result,
+						template[i].psize);
+			ret = crypto_ahash_digest(req);
+			switch (ret) {
+			case 0:
+				break;
+			case -EINPROGRESS:
+			case -EBUSY:
+				ret = wait_for_completion_interruptible(
+					&tresult.completion);
+				if (!ret && !(ret = tresult.err)) {
+					INIT_COMPLETION(tresult.completion);
+					break;
+				}
+				/* fall through */
+			default:
 				printk("digest () failed ret=%d\n", ret);
 				goto out;
 			}
 
-			hexdump(result, crypto_hash_digestsize(tfm));
+			hexdump(result, crypto_ahash_digestsize(tfm));
 			printk("%s\n",
 			       memcmp(result, template[i].digest,
-				      crypto_hash_digestsize(tfm)) ?
+				      crypto_ahash_digestsize(tfm)) ?
 			       "fail" : "pass");
 		}
 	}
 
 out:
-	crypto_free_hash(tfm);
+	ahash_request_free(req);
+out_noreq:
+	crypto_free_ahash(tfm);
 }
 
 static void test_aead(char *algo, int enc, struct aead_testvec *template,
 		      unsigned int tcount)
 {
-	unsigned int ret, i, j, k, temp;
+	unsigned int ret, i, j, k, n, temp;
 	char *q;
 	struct crypto_aead *tfm;
 	char *key;
@@ -344,13 +376,12 @@
 				goto next_one;
 			}
 
-			q = kmap(sg_page(&sg[0])) + sg[0].offset;
+			q = input;
 			hexdump(q, template[i].rlen);
 
 			printk(KERN_INFO "enc/dec: %s\n",
 			       memcmp(q, template[i].result,
 				      template[i].rlen) ? "fail" : "pass");
-			kunmap(sg_page(&sg[0]));
 next_one:
 			if (!template[i].key)
 				kfree(key);
@@ -360,7 +391,6 @@
 	}
 
 	printk(KERN_INFO "\ntesting %s %s across pages (chunking)\n", algo, e);
-	memset(xbuf, 0, XBUFSIZE);
 	memset(axbuf, 0, XBUFSIZE);
 
 	for (i = 0, j = 0; i < tcount; i++) {
@@ -388,6 +418,7 @@
 					goto out;
 			}
 
+			memset(xbuf, 0, XBUFSIZE);
 			sg_init_table(sg, template[i].np);
 			for (k = 0, temp = 0; k < template[i].np; k++) {
 				memcpy(&xbuf[IDX[k]],
@@ -450,7 +481,7 @@
 
 			for (k = 0, temp = 0; k < template[i].np; k++) {
 				printk(KERN_INFO "page %u\n", k);
-				q = kmap(sg_page(&sg[k])) + sg[k].offset;
+				q = &axbuf[IDX[k]];
 				hexdump(q, template[i].tap[k]);
 				printk(KERN_INFO "%s\n",
 				       memcmp(q, template[i].result + temp,
@@ -459,8 +490,15 @@
 					       0 : authsize)) ?
 				       "fail" : "pass");
 
+				for (n = 0; q[template[i].tap[k] + n]; n++)
+					;
+				if (n) {
+					printk("Result buffer corruption %u "
+					       "bytes:\n", n);
+					hexdump(&q[template[i].tap[k]], n);
+				}
+
 				temp += template[i].tap[k];
-				kunmap(sg_page(&sg[k]));
 			}
 		}
 	}
@@ -473,7 +511,7 @@
 static void test_cipher(char *algo, int enc,
 			struct cipher_testvec *template, unsigned int tcount)
 {
-	unsigned int ret, i, j, k, temp;
+	unsigned int ret, i, j, k, n, temp;
 	char *q;
 	struct crypto_ablkcipher *tfm;
 	struct ablkcipher_request *req;
@@ -569,19 +607,17 @@
 				goto out;
 			}
 
-			q = kmap(sg_page(&sg[0])) + sg[0].offset;
+			q = data;
 			hexdump(q, template[i].rlen);
 
 			printk("%s\n",
 			       memcmp(q, template[i].result,
 				      template[i].rlen) ? "fail" : "pass");
-			kunmap(sg_page(&sg[0]));
 		}
 		kfree(data);
 	}
 
 	printk("\ntesting %s %s across pages (chunking)\n", algo, e);
-	memset(xbuf, 0, XBUFSIZE);
 
 	j = 0;
 	for (i = 0; i < tcount; i++) {
@@ -596,6 +632,7 @@
 			printk("test %u (%d bit key):\n",
 			j, template[i].klen * 8);
 
+			memset(xbuf, 0, XBUFSIZE);
 			crypto_ablkcipher_clear_flags(tfm, ~0);
 			if (template[i].wk)
 				crypto_ablkcipher_set_flags(
@@ -649,14 +686,21 @@
 			temp = 0;
 			for (k = 0; k < template[i].np; k++) {
 				printk("page %u\n", k);
-				q = kmap(sg_page(&sg[k])) + sg[k].offset;
+				q = &xbuf[IDX[k]];
 				hexdump(q, template[i].tap[k]);
 				printk("%s\n",
 					memcmp(q, template[i].result + temp,
 						template[i].tap[k]) ? "fail" :
 					"pass");
+
+				for (n = 0; q[template[i].tap[k] + n]; n++)
+					;
+				if (n) {
+					printk("Result buffer corruption %u "
+					       "bytes:\n", n);
+					hexdump(&q[template[i].tap[k]], n);
+				}
 				temp += template[i].tap[k];
-				kunmap(sg_page(&sg[k]));
 			}
 		}
 	}
@@ -1172,6 +1216,14 @@
 		test_cipher("ecb(des3_ede)", DECRYPT, des3_ede_dec_tv_template,
 			    DES3_EDE_DEC_TEST_VECTORS);
 
+		test_cipher("cbc(des3_ede)", ENCRYPT,
+			    des3_ede_cbc_enc_tv_template,
+			    DES3_EDE_CBC_ENC_TEST_VECTORS);
+
+		test_cipher("cbc(des3_ede)", DECRYPT,
+			    des3_ede_cbc_dec_tv_template,
+			    DES3_EDE_CBC_DEC_TEST_VECTORS);
+
 		test_hash("md4", md4_tv_template, MD4_TEST_VECTORS);
 
 		test_hash("sha224", sha224_tv_template, SHA224_TEST_VECTORS);
@@ -1382,6 +1434,14 @@
 			    DES3_EDE_ENC_TEST_VECTORS);
 		test_cipher("ecb(des3_ede)", DECRYPT, des3_ede_dec_tv_template,
 			    DES3_EDE_DEC_TEST_VECTORS);
+
+		test_cipher("cbc(des3_ede)", ENCRYPT,
+			    des3_ede_cbc_enc_tv_template,
+			    DES3_EDE_CBC_ENC_TEST_VECTORS);
+
+		test_cipher("cbc(des3_ede)", DECRYPT,
+			    des3_ede_cbc_dec_tv_template,
+			    DES3_EDE_CBC_DEC_TEST_VECTORS);
 		break;
 
 	case 5:
@@ -1550,7 +1610,7 @@
 	case 29:
 		test_hash("tgr128", tgr128_tv_template, TGR128_TEST_VECTORS);
 		break;
-		
+
 	case 30:
 		test_cipher("ecb(xeta)", ENCRYPT, xeta_enc_tv_template,
 			    XETA_ENC_TEST_VECTORS);
@@ -1615,6 +1675,22 @@
 			    CTS_MODE_DEC_TEST_VECTORS);
 		break;
 
+        case 39:
+		test_hash("rmd128", rmd128_tv_template, RMD128_TEST_VECTORS);
+		break;
+
+        case 40:
+		test_hash("rmd160", rmd160_tv_template, RMD160_TEST_VECTORS);
+		break;
+
+	case 41:
+		test_hash("rmd256", rmd256_tv_template, RMD256_TEST_VECTORS);
+		break;
+
+	case 42:
+		test_hash("rmd320", rmd320_tv_template, RMD320_TEST_VECTORS);
+		break;
+
 	case 100:
 		test_hash("hmac(md5)", hmac_md5_tv_template,
 			  HMAC_MD5_TEST_VECTORS);
@@ -1650,6 +1726,16 @@
 			  XCBC_AES_TEST_VECTORS);
 		break;
 
+	case 107:
+		test_hash("hmac(rmd128)", hmac_rmd128_tv_template,
+			  HMAC_RMD128_TEST_VECTORS);
+		break;
+
+	case 108:
+		test_hash("hmac(rmd160)", hmac_rmd160_tv_template,
+			  HMAC_RMD160_TEST_VECTORS);
+		break;
+
 	case 200:
 		test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0,
 				speed_template_16_24_32);
@@ -1788,6 +1874,22 @@
 		test_hash_speed("sha224", sec, generic_hash_speed_template);
 		if (mode > 300 && mode < 400) break;
 
+	case 314:
+		test_hash_speed("rmd128", sec, generic_hash_speed_template);
+		if (mode > 300 && mode < 400) break;
+
+	case 315:
+		test_hash_speed("rmd160", sec, generic_hash_speed_template);
+		if (mode > 300 && mode < 400) break;
+
+	case 316:
+		test_hash_speed("rmd256", sec, generic_hash_speed_template);
+		if (mode > 300 && mode < 400) break;
+
+	case 317:
+		test_hash_speed("rmd320", sec, generic_hash_speed_template);
+		if (mode > 300 && mode < 400) break;
+
 	case 399:
 		break;
 
diff --git a/crypto/tcrypt.h b/crypto/tcrypt.h
index 47bc0ec..801e0c2 100644
--- a/crypto/tcrypt.h
+++ b/crypto/tcrypt.h
@@ -13,12 +13,6 @@
  * Software Foundation; either version 2 of the License, or (at your option)
  * any later version.
  *
- * 2007-11-13 Added GCM tests
- * 2007-11-13 Added AEAD support
- * 2006-12-07 Added SHA384 HMAC and SHA512 HMAC tests
- * 2004-08-09 Cipher speed tests by Reyk Floeter <reyk@vantronix.net>
- * 2003-09-14 Changes by Kartikey Mahendra Bhatt
- *
  */
 #ifndef _CRYPTO_TCRYPT_H
 #define _CRYPTO_TCRYPT_H
@@ -168,6 +162,271 @@
 		.digest	= "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55"
 			  "\xac\x49\xda\x2e\x21\x07\xb6\x7a",
 	}
+
+};
+
+/*
+ * RIPEMD-128 test vectors from ISO/IEC 10118-3:2004(E)
+ */
+#define RMD128_TEST_VECTORS     10
+
+static struct hash_testvec rmd128_tv_template[] = {
+	{
+		.digest	= "\xcd\xf2\x62\x13\xa1\x50\xdc\x3e"
+			  "\xcb\x61\x0f\x18\xf6\xb3\x8b\x46",
+	}, {
+		.plaintext = "a",
+		.psize	= 1,
+		.digest	= "\x86\xbe\x7a\xfa\x33\x9d\x0f\xc7"
+			  "\xcf\xc7\x85\xe7\x2f\x57\x8d\x33",
+	}, {
+		.plaintext = "abc",
+		.psize	= 3,
+		.digest	= "\xc1\x4a\x12\x19\x9c\x66\xe4\xba"
+			  "\x84\x63\x6b\x0f\x69\x14\x4c\x77",
+	}, {
+		.plaintext = "message digest",
+		.psize	= 14,
+		.digest	= "\x9e\x32\x7b\x3d\x6e\x52\x30\x62"
+			  "\xaf\xc1\x13\x2d\x7d\xf9\xd1\xb8",
+	}, {
+		.plaintext = "abcdefghijklmnopqrstuvwxyz",
+		.psize	= 26,
+		.digest	= "\xfd\x2a\xa6\x07\xf7\x1d\xc8\xf5"
+			  "\x10\x71\x49\x22\xb3\x71\x83\x4e",
+	}, {
+		.plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
+			     "fghijklmnopqrstuvwxyz0123456789",
+		.psize	= 62,
+		.digest	= "\xd1\xe9\x59\xeb\x17\x9c\x91\x1f"
+			  "\xae\xa4\x62\x4c\x60\xc5\xc7\x02",
+	}, {
+		.plaintext = "1234567890123456789012345678901234567890"
+			     "1234567890123456789012345678901234567890",
+		.psize	= 80,
+		.digest	= "\x3f\x45\xef\x19\x47\x32\xc2\xdb"
+			  "\xb2\xc4\xa2\xc7\x69\x79\x5f\xa3",
+        }, {
+		.plaintext = "abcdbcdecdefdefgefghfghighij"
+			     "hijkijkljklmklmnlmnomnopnopq",
+		.psize	= 56,
+		.digest	= "\xa1\xaa\x06\x89\xd0\xfa\xfa\x2d"
+			  "\xdc\x22\xe8\x8b\x49\x13\x3a\x06",
+		.np	= 2,
+		.tap	= { 28, 28 },
+	}, {
+		.plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghi"
+			     "jklmghijklmnhijklmnoijklmnopjklmnopqklmnopqr"
+			     "lmnopqrsmnopqrstnopqrstu",
+		.psize	= 112,
+		.digest	= "\xd4\xec\xc9\x13\xe1\xdf\x77\x6b"
+			  "\xf4\x8d\xe9\xd5\x5b\x1f\x25\x46",
+	}, {
+		.plaintext = "abcdbcdecdefdefgefghfghighijhijk",
+		.psize	= 32,
+		.digest	= "\x13\xfc\x13\xe8\xef\xff\x34\x7d"
+			  "\xe1\x93\xff\x46\xdb\xac\xcf\xd4",
+	}
+};
+
+/*
+ * RIPEMD-160 test vectors from ISO/IEC 10118-3:2004(E)
+ */
+#define RMD160_TEST_VECTORS     10
+
+static struct hash_testvec rmd160_tv_template[] = {
+	{
+		.digest	= "\x9c\x11\x85\xa5\xc5\xe9\xfc\x54\x61\x28"
+			  "\x08\x97\x7e\xe8\xf5\x48\xb2\x25\x8d\x31",
+	}, {
+		.plaintext = "a",
+		.psize	= 1,
+		.digest	= "\x0b\xdc\x9d\x2d\x25\x6b\x3e\xe9\xda\xae"
+			  "\x34\x7b\xe6\xf4\xdc\x83\x5a\x46\x7f\xfe",
+	}, {
+		.plaintext = "abc",
+		.psize	= 3,
+		.digest	= "\x8e\xb2\x08\xf7\xe0\x5d\x98\x7a\x9b\x04"
+			  "\x4a\x8e\x98\xc6\xb0\x87\xf1\x5a\x0b\xfc",
+	}, {
+		.plaintext = "message digest",
+		.psize	= 14,
+		.digest	= "\x5d\x06\x89\xef\x49\xd2\xfa\xe5\x72\xb8"
+			  "\x81\xb1\x23\xa8\x5f\xfa\x21\x59\x5f\x36",
+	}, {
+		.plaintext = "abcdefghijklmnopqrstuvwxyz",
+		.psize	= 26,
+		.digest	= "\xf7\x1c\x27\x10\x9c\x69\x2c\x1b\x56\xbb"
+			  "\xdc\xeb\x5b\x9d\x28\x65\xb3\x70\x8d\xbc",
+	}, {
+		.plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
+			     "fghijklmnopqrstuvwxyz0123456789",
+		.psize	= 62,
+		.digest	= "\xb0\xe2\x0b\x6e\x31\x16\x64\x02\x86\xed"
+			  "\x3a\x87\xa5\x71\x30\x79\xb2\x1f\x51\x89",
+	}, {
+		.plaintext = "1234567890123456789012345678901234567890"
+			     "1234567890123456789012345678901234567890",
+		.psize	= 80,
+		.digest	= "\x9b\x75\x2e\x45\x57\x3d\x4b\x39\xf4\xdb"
+			  "\xd3\x32\x3c\xab\x82\xbf\x63\x32\x6b\xfb",
+        }, {
+		.plaintext = "abcdbcdecdefdefgefghfghighij"
+			     "hijkijkljklmklmnlmnomnopnopq",
+		.psize	= 56,
+		.digest	= "\x12\xa0\x53\x38\x4a\x9c\x0c\x88\xe4\x05"
+			  "\xa0\x6c\x27\xdc\xf4\x9a\xda\x62\xeb\x2b",
+		.np	= 2,
+		.tap	= { 28, 28 },
+	}, {
+		.plaintext = "abcdefghbcdefghicdefghijdefghijkefghijklfghi"
+			     "jklmghijklmnhijklmnoijklmnopjklmnopqklmnopqr"
+			     "lmnopqrsmnopqrstnopqrstu",
+		.psize	= 112,
+		.digest	= "\x6f\x3f\xa3\x9b\x6b\x50\x3c\x38\x4f\x91"
+			  "\x9a\x49\xa7\xaa\x5c\x2c\x08\xbd\xfb\x45",
+	}, {
+		.plaintext = "abcdbcdecdefdefgefghfghighijhijk",
+		.psize	= 32,
+		.digest	= "\x94\xc2\x64\x11\x54\x04\xe6\x33\x79\x0d"
+			  "\xfc\xc8\x7b\x58\x7d\x36\x77\x06\x7d\x9f",
+	}
+};
+
+/*
+ * RIPEMD-256 test vectors
+ */
+#define RMD256_TEST_VECTORS     8
+
+static struct hash_testvec rmd256_tv_template[] = {
+	{
+		.digest	= "\x02\xba\x4c\x4e\x5f\x8e\xcd\x18"
+			  "\x77\xfc\x52\xd6\x4d\x30\xe3\x7a"
+			  "\x2d\x97\x74\xfb\x1e\x5d\x02\x63"
+			  "\x80\xae\x01\x68\xe3\xc5\x52\x2d",
+	}, {
+		.plaintext = "a",
+		.psize	= 1,
+		.digest	= "\xf9\x33\x3e\x45\xd8\x57\xf5\xd9"
+			  "\x0a\x91\xba\xb7\x0a\x1e\xba\x0c"
+			  "\xfb\x1b\xe4\xb0\x78\x3c\x9a\xcf"
+			  "\xcd\x88\x3a\x91\x34\x69\x29\x25",
+	}, {
+		.plaintext = "abc",
+		.psize	= 3,
+		.digest	= "\xaf\xbd\x6e\x22\x8b\x9d\x8c\xbb"
+			  "\xce\xf5\xca\x2d\x03\xe6\xdb\xa1"
+			  "\x0a\xc0\xbc\x7d\xcb\xe4\x68\x0e"
+			  "\x1e\x42\xd2\xe9\x75\x45\x9b\x65",
+	}, {
+		.plaintext = "message digest",
+		.psize	= 14,
+		.digest	= "\x87\xe9\x71\x75\x9a\x1c\xe4\x7a"
+			  "\x51\x4d\x5c\x91\x4c\x39\x2c\x90"
+			  "\x18\xc7\xc4\x6b\xc1\x44\x65\x55"
+			  "\x4a\xfc\xdf\x54\xa5\x07\x0c\x0e",
+	}, {
+		.plaintext = "abcdefghijklmnopqrstuvwxyz",
+		.psize	= 26,
+		.digest	= "\x64\x9d\x30\x34\x75\x1e\xa2\x16"
+			  "\x77\x6b\xf9\xa1\x8a\xcc\x81\xbc"
+			  "\x78\x96\x11\x8a\x51\x97\x96\x87"
+			  "\x82\xdd\x1f\xd9\x7d\x8d\x51\x33",
+	}, {
+		.plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
+			     "fghijklmnopqrstuvwxyz0123456789",
+		.psize	= 62,
+		.digest	= "\x57\x40\xa4\x08\xac\x16\xb7\x20"
+			  "\xb8\x44\x24\xae\x93\x1c\xbb\x1f"
+			  "\xe3\x63\xd1\xd0\xbf\x40\x17\xf1"
+			  "\xa8\x9f\x7e\xa6\xde\x77\xa0\xb8",
+	}, {
+		.plaintext = "1234567890123456789012345678901234567890"
+			     "1234567890123456789012345678901234567890",
+		.psize	= 80,
+		.digest	= "\x06\xfd\xcc\x7a\x40\x95\x48\xaa"
+			  "\xf9\x13\x68\xc0\x6a\x62\x75\xb5"
+			  "\x53\xe3\xf0\x99\xbf\x0e\xa4\xed"
+			  "\xfd\x67\x78\xdf\x89\xa8\x90\xdd",
+        }, {
+		.plaintext = "abcdbcdecdefdefgefghfghighij"
+			     "hijkijkljklmklmnlmnomnopnopq",
+		.psize	= 56,
+		.digest	= "\x38\x43\x04\x55\x83\xaa\xc6\xc8"
+			  "\xc8\xd9\x12\x85\x73\xe7\xa9\x80"
+			  "\x9a\xfb\x2a\x0f\x34\xcc\xc3\x6e"
+			  "\xa9\xe7\x2f\x16\xf6\x36\x8e\x3f",
+		.np	= 2,
+		.tap	= { 28, 28 },
+	}
+};
+
+/*
+ * RIPEMD-320 test vectors
+ */
+#define RMD320_TEST_VECTORS     8
+
+static struct hash_testvec rmd320_tv_template[] = {
+	{
+		.digest	= "\x22\xd6\x5d\x56\x61\x53\x6c\xdc\x75\xc1"
+			  "\xfd\xf5\xc6\xde\x7b\x41\xb9\xf2\x73\x25"
+			  "\xeb\xc6\x1e\x85\x57\x17\x7d\x70\x5a\x0e"
+			  "\xc8\x80\x15\x1c\x3a\x32\xa0\x08\x99\xb8",
+	}, {
+		.plaintext = "a",
+		.psize	= 1,
+		.digest	= "\xce\x78\x85\x06\x38\xf9\x26\x58\xa5\xa5"
+			  "\x85\x09\x75\x79\x92\x6d\xda\x66\x7a\x57"
+			  "\x16\x56\x2c\xfc\xf6\xfb\xe7\x7f\x63\x54"
+			  "\x2f\x99\xb0\x47\x05\xd6\x97\x0d\xff\x5d",
+	}, {
+		.plaintext = "abc",
+		.psize	= 3,
+		.digest	= "\xde\x4c\x01\xb3\x05\x4f\x89\x30\xa7\x9d"
+			  "\x09\xae\x73\x8e\x92\x30\x1e\x5a\x17\x08"
+			  "\x5b\xef\xfd\xc1\xb8\xd1\x16\x71\x3e\x74"
+			  "\xf8\x2f\xa9\x42\xd6\x4c\xdb\xc4\x68\x2d",
+	}, {
+		.plaintext = "message digest",
+		.psize	= 14,
+		.digest	= "\x3a\x8e\x28\x50\x2e\xd4\x5d\x42\x2f\x68"
+			  "\x84\x4f\x9d\xd3\x16\xe7\xb9\x85\x33\xfa"
+			  "\x3f\x2a\x91\xd2\x9f\x84\xd4\x25\xc8\x8d"
+			  "\x6b\x4e\xff\x72\x7d\xf6\x6a\x7c\x01\x97",
+	}, {
+		.plaintext = "abcdefghijklmnopqrstuvwxyz",
+		.psize	= 26,
+		.digest	= "\xca\xbd\xb1\x81\x0b\x92\x47\x0a\x20\x93"
+			  "\xaa\x6b\xce\x05\x95\x2c\x28\x34\x8c\xf4"
+			  "\x3f\xf6\x08\x41\x97\x51\x66\xbb\x40\xed"
+			  "\x23\x40\x04\xb8\x82\x44\x63\xe6\xb0\x09",
+	}, {
+		.plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
+			     "fghijklmnopqrstuvwxyz0123456789",
+		.psize	= 62,
+		.digest	= "\xed\x54\x49\x40\xc8\x6d\x67\xf2\x50\xd2"
+			  "\x32\xc3\x0b\x7b\x3e\x57\x70\xe0\xc6\x0c"
+			  "\x8c\xb9\xa4\xca\xfe\x3b\x11\x38\x8a\xf9"
+			  "\x92\x0e\x1b\x99\x23\x0b\x84\x3c\x86\xa4",
+	}, {
+		.plaintext = "1234567890123456789012345678901234567890"
+			     "1234567890123456789012345678901234567890",
+		.psize	= 80,
+		.digest	= "\x55\x78\x88\xaf\x5f\x6d\x8e\xd6\x2a\xb6"
+			  "\x69\x45\xc6\xd2\xa0\xa4\x7e\xcd\x53\x41"
+			  "\xe9\x15\xeb\x8f\xea\x1d\x05\x24\x95\x5f"
+			  "\x82\x5d\xc7\x17\xe4\xa0\x08\xab\x2d\x42",
+        }, {
+		.plaintext = "abcdbcdecdefdefgefghfghighij"
+			     "hijkijkljklmklmnlmnomnopnopq",
+		.psize	= 56,
+		.digest	= "\xd0\x34\xa7\x95\x0c\xf7\x22\x02\x1b\xa4"
+			  "\xb8\x4d\xf7\x69\xa5\xde\x20\x60\xe2\x59"
+			  "\xdf\x4c\x9b\xb4\xa4\x26\x8c\x0e\x93\x5b"
+			  "\xbc\x74\x70\xa9\x69\xc9\xd0\x72\xa1\xac",
+		.np	= 2,
+		.tap	= { 28, 28 },
+	}
 };
 
 /*
@@ -817,6 +1076,168 @@
 };
 
 /*
+ * HMAC-RIPEMD128 test vectors from RFC2286
+ */
+#define HMAC_RMD128_TEST_VECTORS	7
+
+static struct hash_testvec hmac_rmd128_tv_template[] = {
+	{
+		.key	= "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+		.ksize	= 16,
+		.plaintext = "Hi There",
+		.psize	= 8,
+		.digest	= "\xfb\xf6\x1f\x94\x92\xaa\x4b\xbf"
+			  "\x81\xc1\x72\xe8\x4e\x07\x34\xdb",
+	}, {
+		.key	= "Jefe",
+		.ksize	= 4,
+		.plaintext = "what do ya want for nothing?",
+		.psize	= 28,
+		.digest	= "\x87\x5f\x82\x88\x62\xb6\xb3\x34"
+			  "\xb4\x27\xc5\x5f\x9f\x7f\xf0\x9b",
+		.np	= 2,
+		.tap	= { 14, 14 },
+	}, {
+		.key	= "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+		.ksize	= 16,
+		.plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+			"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+			"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+			"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+		.psize	= 50,
+		.digest	= "\x09\xf0\xb2\x84\x6d\x2f\x54\x3d"
+			  "\xa3\x63\xcb\xec\x8d\x62\xa3\x8d",
+	}, {
+		.key	= "\x01\x02\x03\x04\x05\x06\x07\x08"
+			  "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+			  "\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+		.ksize	= 25,
+		.plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+			"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+			"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+			"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+		.psize	= 50,
+		.digest	= "\xbd\xbb\xd7\xcf\x03\xe4\x4b\x5a"
+			  "\xa6\x0a\xf8\x15\xbe\x4d\x22\x94",
+	}, {
+		.key	= "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+		.ksize	= 16,
+		.plaintext = "Test With Truncation",
+		.psize	= 20,
+		.digest	= "\xe7\x98\x08\xf2\x4b\x25\xfd\x03"
+			  "\x1c\x15\x5f\x0d\x55\x1d\x9a\x3a",
+	}, {
+		.key	= "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa",
+		.ksize	= 80,
+		.plaintext = "Test Using Larger Than Block-Size Key - Hash Key First",
+		.psize	= 54,
+		.digest	= "\xdc\x73\x29\x28\xde\x98\x10\x4a"
+			  "\x1f\x59\xd3\x73\xc1\x50\xac\xbb",
+	}, {
+		.key	= "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa",
+		.ksize	= 80,
+		.plaintext = "Test Using Larger Than Block-Size Key and Larger Than One "
+			   "Block-Size Data",
+		.psize	= 73,
+		.digest	= "\x5c\x6b\xec\x96\x79\x3e\x16\xd4"
+			  "\x06\x90\xc2\x37\x63\x5f\x30\xc5",
+	},
+};
+
+/*
+ * HMAC-RIPEMD160 test vectors from RFC2286
+ */
+#define HMAC_RMD160_TEST_VECTORS	7
+
+static struct hash_testvec hmac_rmd160_tv_template[] = {
+	{
+		.key	= "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+		.ksize	= 20,
+		.plaintext = "Hi There",
+		.psize	= 8,
+		.digest	= "\x24\xcb\x4b\xd6\x7d\x20\xfc\x1a\x5d\x2e"
+			  "\xd7\x73\x2d\xcc\x39\x37\x7f\x0a\x56\x68",
+	}, {
+		.key	= "Jefe",
+		.ksize	= 4,
+		.plaintext = "what do ya want for nothing?",
+		.psize	= 28,
+		.digest	= "\xdd\xa6\xc0\x21\x3a\x48\x5a\x9e\x24\xf4"
+			  "\x74\x20\x64\xa7\xf0\x33\xb4\x3c\x40\x69",
+		.np	= 2,
+		.tap	= { 14, 14 },
+	}, {
+		.key	= "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+		.ksize	= 20,
+		.plaintext = "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+			"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+			"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+			"\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+		.psize	= 50,
+		.digest	= "\xb0\xb1\x05\x36\x0d\xe7\x59\x96\x0a\xb4"
+			  "\xf3\x52\x98\xe1\x16\xe2\x95\xd8\xe7\xc1",
+	}, {
+		.key	= "\x01\x02\x03\x04\x05\x06\x07\x08"
+			  "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+			  "\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+		.ksize	= 25,
+		.plaintext = "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+			"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+			"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+			"\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+		.psize	= 50,
+		.digest	= "\xd5\xca\x86\x2f\x4d\x21\xd5\xe6\x10\xe1"
+			  "\x8b\x4c\xf1\xbe\xb9\x7a\x43\x65\xec\xf4",
+	}, {
+		.key	= "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+		.ksize	= 20,
+		.plaintext = "Test With Truncation",
+		.psize	= 20,
+		.digest	= "\x76\x19\x69\x39\x78\xf9\x1d\x90\x53\x9a"
+			  "\xe7\x86\x50\x0f\xf3\xd8\xe0\x51\x8e\x39",
+	}, {
+		.key	= "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa",
+		.ksize	= 80,
+		.plaintext = "Test Using Larger Than Block-Size Key - Hash Key First",
+		.psize	= 54,
+		.digest	= "\x64\x66\xca\x07\xac\x5e\xac\x29\xe1\xbd"
+			  "\x52\x3e\x5a\xda\x76\x05\xb7\x91\xfd\x8b",
+	}, {
+		.key	= "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+			"\xaa\xaa",
+		.ksize	= 80,
+		.plaintext = "Test Using Larger Than Block-Size Key and Larger Than One "
+			   "Block-Size Data",
+		.psize	= 73,
+		.digest	= "\x69\xea\x60\x79\x8d\x71\x61\x6c\xce\x5f"
+			  "\xd0\x87\x1e\x23\x75\x4c\xd7\x5d\x5a\x0a",
+	},
+};
+
+/*
  * HMAC-SHA1 test vectors from RFC2202
  */
 #define HMAC_SHA1_TEST_VECTORS	7
@@ -1442,6 +1863,8 @@
 #define DES_CBC_DEC_TEST_VECTORS	4
 #define DES3_EDE_ENC_TEST_VECTORS	3
 #define DES3_EDE_DEC_TEST_VECTORS	3
+#define DES3_EDE_CBC_ENC_TEST_VECTORS	1
+#define DES3_EDE_CBC_DEC_TEST_VECTORS	1
 
 static struct cipher_testvec des_enc_tv_template[] = {
 	{ /* From Applied Cryptography */
@@ -1680,9 +2103,6 @@
 	},
 };
 
-/*
- * We really need some more test vectors, especially for DES3 CBC.
- */
 static struct cipher_testvec des3_ede_enc_tv_template[] = {
 	{ /* These are from openssl */
 		.key	= "\x01\x23\x45\x67\x89\xab\xcd\xef"
@@ -1745,6 +2165,94 @@
 	},
 };
 
+static struct cipher_testvec des3_ede_cbc_enc_tv_template[] = {
+	{ /* Generated from openssl */
+		.key	= "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24"
+			  "\x44\x4D\x99\x5A\x12\xD6\x40\xC0"
+			  "\xEA\xC2\x84\xE8\x14\x95\xDB\xE8",
+		.klen	= 24,
+		.iv	= "\x7D\x33\x88\x93\x0F\x93\xB2\x42",
+		.input	= "\x6f\x54\x20\x6f\x61\x4d\x79\x6e"
+			  "\x53\x20\x63\x65\x65\x72\x73\x74"
+			  "\x54\x20\x6f\x6f\x4d\x20\x6e\x61"
+			  "\x20\x79\x65\x53\x72\x63\x74\x65"
+			  "\x20\x73\x6f\x54\x20\x6f\x61\x4d"
+			  "\x79\x6e\x53\x20\x63\x65\x65\x72"
+			  "\x73\x74\x54\x20\x6f\x6f\x4d\x20"
+			  "\x6e\x61\x20\x79\x65\x53\x72\x63"
+			  "\x74\x65\x20\x73\x6f\x54\x20\x6f"
+			  "\x61\x4d\x79\x6e\x53\x20\x63\x65"
+			  "\x65\x72\x73\x74\x54\x20\x6f\x6f"
+			  "\x4d\x20\x6e\x61\x20\x79\x65\x53"
+			  "\x72\x63\x74\x65\x20\x73\x6f\x54"
+			  "\x20\x6f\x61\x4d\x79\x6e\x53\x20"
+			  "\x63\x65\x65\x72\x73\x74\x54\x20"
+			  "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79",
+		.ilen	= 128,
+		.result	= "\x0e\x2d\xb6\x97\x3c\x56\x33\xf4"
+			  "\x67\x17\x21\xc7\x6e\x8a\xd5\x49"
+			  "\x74\xb3\x49\x05\xc5\x1c\xd0\xed"
+			  "\x12\x56\x5c\x53\x96\xb6\x00\x7d"
+			  "\x90\x48\xfc\xf5\x8d\x29\x39\xcc"
+			  "\x8a\xd5\x35\x18\x36\x23\x4e\xd7"
+			  "\x76\xd1\xda\x0c\x94\x67\xbb\x04"
+			  "\x8b\xf2\x03\x6c\xa8\xcf\xb6\xea"
+			  "\x22\x64\x47\xaa\x8f\x75\x13\xbf"
+			  "\x9f\xc2\xc3\xf0\xc9\x56\xc5\x7a"
+			  "\x71\x63\x2e\x89\x7b\x1e\x12\xca"
+			  "\xe2\x5f\xaf\xd8\xa4\xf8\xc9\x7a"
+			  "\xd6\xf9\x21\x31\x62\x44\x45\xa6"
+			  "\xd6\xbc\x5a\xd3\x2d\x54\x43\xcc"
+			  "\x9d\xde\xa5\x70\xe9\x42\x45\x8a"
+			  "\x6b\xfa\xb1\x91\x13\xb0\xd9\x19",
+		.rlen	= 128,
+	},
+};
+
+static struct cipher_testvec des3_ede_cbc_dec_tv_template[] = {
+	{ /* Generated from openssl */
+		.key	= "\xE9\xC0\xFF\x2E\x76\x0B\x64\x24"
+			  "\x44\x4D\x99\x5A\x12\xD6\x40\xC0"
+			  "\xEA\xC2\x84\xE8\x14\x95\xDB\xE8",
+		.klen	= 24,
+		.iv	= "\x7D\x33\x88\x93\x0F\x93\xB2\x42",
+		.input	= "\x0e\x2d\xb6\x97\x3c\x56\x33\xf4"
+			  "\x67\x17\x21\xc7\x6e\x8a\xd5\x49"
+			  "\x74\xb3\x49\x05\xc5\x1c\xd0\xed"
+			  "\x12\x56\x5c\x53\x96\xb6\x00\x7d"
+			  "\x90\x48\xfc\xf5\x8d\x29\x39\xcc"
+			  "\x8a\xd5\x35\x18\x36\x23\x4e\xd7"
+			  "\x76\xd1\xda\x0c\x94\x67\xbb\x04"
+			  "\x8b\xf2\x03\x6c\xa8\xcf\xb6\xea"
+			  "\x22\x64\x47\xaa\x8f\x75\x13\xbf"
+			  "\x9f\xc2\xc3\xf0\xc9\x56\xc5\x7a"
+			  "\x71\x63\x2e\x89\x7b\x1e\x12\xca"
+			  "\xe2\x5f\xaf\xd8\xa4\xf8\xc9\x7a"
+			  "\xd6\xf9\x21\x31\x62\x44\x45\xa6"
+			  "\xd6\xbc\x5a\xd3\x2d\x54\x43\xcc"
+			  "\x9d\xde\xa5\x70\xe9\x42\x45\x8a"
+			  "\x6b\xfa\xb1\x91\x13\xb0\xd9\x19",
+		.ilen	= 128,
+		.result	= "\x6f\x54\x20\x6f\x61\x4d\x79\x6e"
+			  "\x53\x20\x63\x65\x65\x72\x73\x74"
+			  "\x54\x20\x6f\x6f\x4d\x20\x6e\x61"
+			  "\x20\x79\x65\x53\x72\x63\x74\x65"
+			  "\x20\x73\x6f\x54\x20\x6f\x61\x4d"
+			  "\x79\x6e\x53\x20\x63\x65\x65\x72"
+			  "\x73\x74\x54\x20\x6f\x6f\x4d\x20"
+			  "\x6e\x61\x20\x79\x65\x53\x72\x63"
+			  "\x74\x65\x20\x73\x6f\x54\x20\x6f"
+			  "\x61\x4d\x79\x6e\x53\x20\x63\x65"
+			  "\x65\x72\x73\x74\x54\x20\x6f\x6f"
+			  "\x4d\x20\x6e\x61\x20\x79\x65\x53"
+			  "\x72\x63\x74\x65\x20\x73\x6f\x54"
+			  "\x20\x6f\x61\x4d\x79\x6e\x53\x20"
+			  "\x63\x65\x65\x72\x73\x74\x54\x20"
+			  "\x6f\x6f\x4d\x20\x6e\x61\x0a\x79",
+		.rlen	= 128,
+	},
+};
+
 /*
  * Blowfish test vectors.
  */
diff --git a/drivers/Makefile b/drivers/Makefile
index f65deda..fda4467 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -10,6 +10,7 @@
 obj-$(CONFIG_PARISC)		+= parisc/
 obj-$(CONFIG_RAPIDIO)		+= rapidio/
 obj-y				+= video/
+obj-y				+= gpu/
 obj-$(CONFIG_ACPI)		+= acpi/
 # PnP must come after ACPI since it will eventually need to check if acpi
 # was used and do nothing if so
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 57a4364..499ccc6 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -885,7 +885,8 @@
 		/* set the min alignment and padding */
 		blk_queue_update_dma_alignment(sdev->request_queue,
 					       ATA_DMA_PAD_SZ - 1);
-		blk_queue_dma_pad(sdev->request_queue, ATA_DMA_PAD_SZ - 1);
+		blk_queue_update_dma_pad(sdev->request_queue,
+					 ATA_DMA_PAD_SZ - 1);
 
 		/* configure draining */
 		buf = kmalloc(ATAPI_MAX_DRAIN, q->bounce_gfp | GFP_KERNEL);
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index cd03473..a002a38 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -6628,15 +6628,18 @@
  * DAC960_gam_ioctl is the ioctl function for performing RAID operations.
 */
 
-static int DAC960_gam_ioctl(struct inode *inode, struct file *file,
-			    unsigned int Request, unsigned long Argument)
+static long DAC960_gam_ioctl(struct file *file, unsigned int Request,
+						unsigned long Argument)
 {
-  int ErrorCode = 0;
+  long ErrorCode = 0;
   if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+  lock_kernel();
   switch (Request)
     {
     case DAC960_IOCTL_GET_CONTROLLER_COUNT:
-      return DAC960_ControllerCount;
+      ErrorCode = DAC960_ControllerCount;
+      break;
     case DAC960_IOCTL_GET_CONTROLLER_INFO:
       {
 	DAC960_ControllerInfo_T __user *UserSpaceControllerInfo =
@@ -6644,15 +6647,20 @@
 	DAC960_ControllerInfo_T ControllerInfo;
 	DAC960_Controller_T *Controller;
 	int ControllerNumber;
-	if (UserSpaceControllerInfo == NULL) return -EINVAL;
-	ErrorCode = get_user(ControllerNumber,
+	if (UserSpaceControllerInfo == NULL)
+		ErrorCode = -EINVAL;
+	else ErrorCode = get_user(ControllerNumber,
 			     &UserSpaceControllerInfo->ControllerNumber);
-	if (ErrorCode != 0) return ErrorCode;
+	if (ErrorCode != 0)
+		break;;
+	ErrorCode = -ENXIO;
 	if (ControllerNumber < 0 ||
-	    ControllerNumber > DAC960_ControllerCount - 1)
-	  return -ENXIO;
+	    ControllerNumber > DAC960_ControllerCount - 1) {
+	  break;
+	}
 	Controller = DAC960_Controllers[ControllerNumber];
-	if (Controller == NULL) return -ENXIO;
+	if (Controller == NULL)
+		break;;
 	memset(&ControllerInfo, 0, sizeof(DAC960_ControllerInfo_T));
 	ControllerInfo.ControllerNumber = ControllerNumber;
 	ControllerInfo.FirmwareType = Controller->FirmwareType;
@@ -6665,8 +6673,9 @@
 	ControllerInfo.PCI_Address = Controller->PCI_Address;
 	strcpy(ControllerInfo.ModelName, Controller->ModelName);
 	strcpy(ControllerInfo.FirmwareVersion, Controller->FirmwareVersion);
-	return (copy_to_user(UserSpaceControllerInfo, &ControllerInfo,
+	ErrorCode = (copy_to_user(UserSpaceControllerInfo, &ControllerInfo,
 			     sizeof(DAC960_ControllerInfo_T)) ? -EFAULT : 0);
+	break;
       }
     case DAC960_IOCTL_V1_EXECUTE_COMMAND:
       {
@@ -6684,30 +6693,39 @@
 	int ControllerNumber, DataTransferLength;
 	unsigned char *DataTransferBuffer = NULL;
 	dma_addr_t DataTransferBufferDMA;
-	if (UserSpaceUserCommand == NULL) return -EINVAL;
+	if (UserSpaceUserCommand == NULL) {
+		ErrorCode = -EINVAL;
+		break;
+	}
 	if (copy_from_user(&UserCommand, UserSpaceUserCommand,
 				   sizeof(DAC960_V1_UserCommand_T))) {
 		ErrorCode = -EFAULT;
-		goto Failure1a;
+		break;
 	}
 	ControllerNumber = UserCommand.ControllerNumber;
+    	ErrorCode = -ENXIO;
 	if (ControllerNumber < 0 ||
 	    ControllerNumber > DAC960_ControllerCount - 1)
-	  return -ENXIO;
+	    	break;
 	Controller = DAC960_Controllers[ControllerNumber];
-	if (Controller == NULL) return -ENXIO;
-	if (Controller->FirmwareType != DAC960_V1_Controller) return -EINVAL;
+	if (Controller == NULL)
+		break;
+	ErrorCode = -EINVAL;
+	if (Controller->FirmwareType != DAC960_V1_Controller)
+		break;
 	CommandOpcode = UserCommand.CommandMailbox.Common.CommandOpcode;
 	DataTransferLength = UserCommand.DataTransferLength;
-	if (CommandOpcode & 0x80) return -EINVAL;
+	if (CommandOpcode & 0x80)
+		break;
 	if (CommandOpcode == DAC960_V1_DCDB)
 	  {
 	    if (copy_from_user(&DCDB, UserCommand.DCDB,
 			       sizeof(DAC960_V1_DCDB_T))) {
 		ErrorCode = -EFAULT;
-		goto Failure1a;
+		break;
 	    }
-	    if (DCDB.Channel >= DAC960_V1_MaxChannels) return -EINVAL;
+	    if (DCDB.Channel >= DAC960_V1_MaxChannels)
+	    		break;
 	    if (!((DataTransferLength == 0 &&
 		   DCDB.Direction
 		   == DAC960_V1_DCDB_NoDataTransfer) ||
@@ -6717,38 +6735,37 @@
 		  (DataTransferLength < 0 &&
 		   DCDB.Direction
 		   == DAC960_V1_DCDB_DataTransferSystemToDevice)))
-	      return -EINVAL;
+		   	break;
 	    if (((DCDB.TransferLengthHigh4 << 16) | DCDB.TransferLength)
 		!= abs(DataTransferLength))
-	      return -EINVAL;
+			break;
 	    DCDB_IOBUF = pci_alloc_consistent(Controller->PCIDevice,
 			sizeof(DAC960_V1_DCDB_T), &DCDB_IOBUFDMA);
-	    if (DCDB_IOBUF == NULL)
-			return -ENOMEM;
+	    if (DCDB_IOBUF == NULL) {
+	    		ErrorCode = -ENOMEM;
+			break;
+		}
 	  }
+	ErrorCode = -ENOMEM;
 	if (DataTransferLength > 0)
 	  {
 	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
 				DataTransferLength, &DataTransferBufferDMA);
-	    if (DataTransferBuffer == NULL) {
-		ErrorCode = -ENOMEM;
-		goto Failure1;
-	    }
+	    if (DataTransferBuffer == NULL)
+	    	break;
 	    memset(DataTransferBuffer, 0, DataTransferLength);
 	  }
 	else if (DataTransferLength < 0)
 	  {
 	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
 				-DataTransferLength, &DataTransferBufferDMA);
-	    if (DataTransferBuffer == NULL) {
-		ErrorCode = -ENOMEM;
-		goto Failure1;
-	    }
+	    if (DataTransferBuffer == NULL)
+	    	break;
 	    if (copy_from_user(DataTransferBuffer,
 			       UserCommand.DataTransferBuffer,
 			       -DataTransferLength)) {
 		ErrorCode = -EFAULT;
-		goto Failure1;
+		break;
 	    }
 	  }
 	if (CommandOpcode == DAC960_V1_DCDB)
@@ -6825,8 +6842,7 @@
 	if (DCDB_IOBUF != NULL)
 	  pci_free_consistent(Controller->PCIDevice, sizeof(DAC960_V1_DCDB_T),
 			DCDB_IOBUF, DCDB_IOBUFDMA);
-      Failure1a:
-	return ErrorCode;
+      	break;
       }
     case DAC960_IOCTL_V2_EXECUTE_COMMAND:
       {
@@ -6844,32 +6860,43 @@
 	dma_addr_t DataTransferBufferDMA;
 	unsigned char *RequestSenseBuffer = NULL;
 	dma_addr_t RequestSenseBufferDMA;
-	if (UserSpaceUserCommand == NULL) return -EINVAL;
+
+	ErrorCode = -EINVAL;
+	if (UserSpaceUserCommand == NULL)
+		break;
 	if (copy_from_user(&UserCommand, UserSpaceUserCommand,
 			   sizeof(DAC960_V2_UserCommand_T))) {
 		ErrorCode = -EFAULT;
-		goto Failure2a;
+		break;
 	}
+	ErrorCode = -ENXIO;
 	ControllerNumber = UserCommand.ControllerNumber;
 	if (ControllerNumber < 0 ||
 	    ControllerNumber > DAC960_ControllerCount - 1)
-	  return -ENXIO;
+	    	break;
 	Controller = DAC960_Controllers[ControllerNumber];
-	if (Controller == NULL) return -ENXIO;
-	if (Controller->FirmwareType != DAC960_V2_Controller) return -EINVAL;
+	if (Controller == NULL)
+		break;
+	if (Controller->FirmwareType != DAC960_V2_Controller){
+		ErrorCode = -EINVAL;
+		break;
+	}
 	DataTransferLength = UserCommand.DataTransferLength;
+    	ErrorCode = -ENOMEM;
 	if (DataTransferLength > 0)
 	  {
 	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
 				DataTransferLength, &DataTransferBufferDMA);
-	    if (DataTransferBuffer == NULL) return -ENOMEM;
+	    if (DataTransferBuffer == NULL)
+	    	break;
 	    memset(DataTransferBuffer, 0, DataTransferLength);
 	  }
 	else if (DataTransferLength < 0)
 	  {
 	    DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
 				-DataTransferLength, &DataTransferBufferDMA);
-	    if (DataTransferBuffer == NULL) return -ENOMEM;
+	    if (DataTransferBuffer == NULL)
+	    	break;
 	    if (copy_from_user(DataTransferBuffer,
 			       UserCommand.DataTransferBuffer,
 			       -DataTransferLength)) {
@@ -6979,8 +7006,7 @@
 	if (RequestSenseBuffer != NULL)
 	  pci_free_consistent(Controller->PCIDevice, RequestSenseLength,
 		RequestSenseBuffer, RequestSenseBufferDMA);
-      Failure2a:
-	return ErrorCode;
+        break;
       }
     case DAC960_IOCTL_V2_GET_HEALTH_STATUS:
       {
@@ -6990,21 +7016,33 @@
 	DAC960_V2_HealthStatusBuffer_T HealthStatusBuffer;
 	DAC960_Controller_T *Controller;
 	int ControllerNumber;
-	if (UserSpaceGetHealthStatus == NULL) return -EINVAL;
+	if (UserSpaceGetHealthStatus == NULL) {
+		ErrorCode = -EINVAL;
+		break;
+	}
 	if (copy_from_user(&GetHealthStatus, UserSpaceGetHealthStatus,
-			   sizeof(DAC960_V2_GetHealthStatus_T)))
-		return -EFAULT;
+			   sizeof(DAC960_V2_GetHealthStatus_T))) {
+		ErrorCode = -EFAULT;
+		break;
+	}
+	ErrorCode = -ENXIO;
 	ControllerNumber = GetHealthStatus.ControllerNumber;
 	if (ControllerNumber < 0 ||
 	    ControllerNumber > DAC960_ControllerCount - 1)
-	  return -ENXIO;
+		    break;
 	Controller = DAC960_Controllers[ControllerNumber];
-	if (Controller == NULL) return -ENXIO;
-	if (Controller->FirmwareType != DAC960_V2_Controller) return -EINVAL;
+	if (Controller == NULL)
+		break;
+	if (Controller->FirmwareType != DAC960_V2_Controller) {
+		ErrorCode = -EINVAL;
+		break;
+	}
 	if (copy_from_user(&HealthStatusBuffer,
 			   GetHealthStatus.HealthStatusBuffer,
-			   sizeof(DAC960_V2_HealthStatusBuffer_T)))
-		return -EFAULT;
+			   sizeof(DAC960_V2_HealthStatusBuffer_T))) {
+		ErrorCode = -EFAULT;
+		break;
+	}
 	while (Controller->V2.HealthStatusBuffer->StatusChangeCounter
 	       == HealthStatusBuffer.StatusChangeCounter &&
 	       Controller->V2.HealthStatusBuffer->NextEventSequenceNumber
@@ -7012,21 +7050,28 @@
 	  {
 	    interruptible_sleep_on_timeout(&Controller->HealthStatusWaitQueue,
 					   DAC960_MonitoringTimerInterval);
-	    if (signal_pending(current)) return -EINTR;
+	    if (signal_pending(current)) {
+	    	ErrorCode = -EINTR;
+	    	break;
+	    }
 	  }
 	if (copy_to_user(GetHealthStatus.HealthStatusBuffer,
 			 Controller->V2.HealthStatusBuffer,
 			 sizeof(DAC960_V2_HealthStatusBuffer_T)))
-		return -EFAULT;
-	return 0;
+		ErrorCode = -EFAULT;
+	else
+		ErrorCode =  0;
       }
+      default:
+	ErrorCode = -ENOTTY;
     }
-  return -EINVAL;
+  unlock_kernel();
+  return ErrorCode;
 }
 
 static const struct file_operations DAC960_gam_fops = {
 	.owner		= THIS_MODULE,
-	.ioctl		= DAC960_gam_ioctl
+	.unlocked_ioctl	= DAC960_gam_ioctl
 };
 
 static struct miscdevice DAC960_gam_dev = {
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index 41f818b..2f17462 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -1003,7 +1003,7 @@
 	 * Enough people have their dip switches set backwards to
 	 * warrant a loud message for this special case.
 	 */
-	aoemajor = be16_to_cpu(get_unaligned(&h->major));
+	aoemajor = get_unaligned_be16(&h->major);
 	if (aoemajor == 0xfff) {
 		printk(KERN_ERR "aoe: Warning: shelf address is all ones.  "
 			"Check shelf dip switches.\n");
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
index 8b9549a..27455ee 100644
--- a/drivers/block/paride/pt.c
+++ b/drivers/block/paride/pt.c
@@ -146,6 +146,7 @@
 #include <linux/mtio.h>
 #include <linux/device.h>
 #include <linux/sched.h>	/* current, TASK_*, schedule_timeout() */
+#include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
 
@@ -189,8 +190,7 @@
 #define ATAPI_LOG_SENSE		0x4d
 
 static int pt_open(struct inode *inode, struct file *file);
-static int pt_ioctl(struct inode *inode, struct file *file,
-		    unsigned int cmd, unsigned long arg);
+static long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 static int pt_release(struct inode *inode, struct file *file);
 static ssize_t pt_read(struct file *filp, char __user *buf,
 		       size_t count, loff_t * ppos);
@@ -236,7 +236,7 @@
 	.owner = THIS_MODULE,
 	.read = pt_read,
 	.write = pt_write,
-	.ioctl = pt_ioctl,
+	.unlocked_ioctl = pt_ioctl,
 	.open = pt_open,
 	.release = pt_release,
 };
@@ -685,8 +685,7 @@
 	return err;
 }
 
-static int pt_ioctl(struct inode *inode, struct file *file,
-	 unsigned int cmd, unsigned long arg)
+static long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct pt_unit *tape = file->private_data;
 	struct mtop __user *p = (void __user *)arg;
@@ -700,23 +699,26 @@
 		switch (mtop.mt_op) {
 
 		case MTREW:
+			lock_kernel();
 			pt_rewind(tape);
+			unlock_kernel();
 			return 0;
 
 		case MTWEOF:
+			lock_kernel();
 			pt_write_fm(tape);
+			unlock_kernel();
 			return 0;
 
 		default:
-			printk("%s: Unimplemented mt_op %d\n", tape->name,
+			/* FIXME: rate limit ?? */
+			printk(KERN_DEBUG "%s: Unimplemented mt_op %d\n", tape->name,
 			       mtop.mt_op);
 			return -EINVAL;
 		}
 
 	default:
-		printk("%s: Unimplemented ioctl 0x%x\n", tape->name, cmd);
-		return -EINVAL;
-
+		return -ENOTTY;
 	}
 }
 
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 3ba1df9..45bee91 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -49,6 +49,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/kthread.h>
+#include <linux/smp_lock.h>
 #include <linux/errno.h>
 #include <linux/spinlock.h>
 #include <linux/file.h>
@@ -2079,7 +2080,6 @@
 	unsigned char buf[64];
 	int ret;
 
-	memset(buf, 0, sizeof(buf));
 	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
 	cgc.sense = &sense;
 	cgc.buflen = pd->mode_offset + 12;
@@ -2126,7 +2126,6 @@
 	unsigned char *cap_buf;
 	int ret, offset;
 
-	memset(buf, 0, sizeof(buf));
 	cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset];
 	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN);
 	cgc.sense = &sense;
@@ -2633,11 +2632,12 @@
 
 
 
-static int pkt_merge_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *bvec)
+static int pkt_merge_bvec(struct request_queue *q, struct bvec_merge_data *bmd,
+			  struct bio_vec *bvec)
 {
 	struct pktcdvd_device *pd = q->queuedata;
-	sector_t zone = ZONE(bio->bi_sector, pd);
-	int used = ((bio->bi_sector - zone) << 9) + bio->bi_size;
+	sector_t zone = ZONE(bmd->bi_sector, pd);
+	int used = ((bmd->bi_sector - zone) << 9) + bmd->bi_size;
 	int remaining = (pd->settings.size << 9) - used;
 	int remaining2;
 
@@ -2645,7 +2645,7 @@
 	 * A bio <= PAGE_SIZE must be allowed. If it crosses a packet
 	 * boundary, pkt_make_request() will split the bio.
 	 */
-	remaining2 = PAGE_SIZE - bio->bi_size;
+	remaining2 = PAGE_SIZE - bmd->bi_size;
 	remaining = max(remaining, remaining2);
 
 	BUG_ON(remaining < 0);
@@ -2796,9 +2796,14 @@
 	return ret;
 }
 
-static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long pkt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct pktcdvd_device *pd;
+	long ret;
+
+	lock_kernel();
+	pd = inode->i_bdev->bd_disk->private_data;
 
 	VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode));
 
@@ -2811,7 +2816,8 @@
 	case CDROM_LAST_WRITTEN:
 	case CDROM_SEND_PACKET:
 	case SCSI_IOCTL_SEND_COMMAND:
-		return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);
+		ret = blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);
+		break;
 
 	case CDROMEJECT:
 		/*
@@ -2820,14 +2826,15 @@
 		 */
 		if (pd->refcnt == 1)
 			pkt_lock_door(pd, 0);
-		return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);
+		ret = blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);
+		break;
 
 	default:
 		VPRINTK(DRIVER_NAME": Unknown ioctl for %s (%x)\n", pd->name, cmd);
-		return -ENOTTY;
+		ret = -ENOTTY;
 	}
-
-	return 0;
+	unlock_kernel();
+	return ret;
 }
 
 static int pkt_media_changed(struct gendisk *disk)
@@ -2849,7 +2856,7 @@
 	.owner =		THIS_MODULE,
 	.open =			pkt_open,
 	.release =		pkt_close,
-	.ioctl =		pkt_ioctl,
+	.unlocked_ioctl =	pkt_ioctl,
 	.media_changed =	pkt_media_changed,
 };
 
@@ -3014,7 +3021,8 @@
 	mutex_unlock(&ctl_mutex);
 }
 
-static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long pkt_ctl_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
 {
 	void __user *argp = (void __user *)arg;
 	struct pkt_ctrl_command ctrl_cmd;
@@ -3031,16 +3039,22 @@
 	case PKT_CTRL_CMD_SETUP:
 		if (!capable(CAP_SYS_ADMIN))
 			return -EPERM;
+		lock_kernel();
 		ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev);
 		ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev);
+		unlock_kernel();
 		break;
 	case PKT_CTRL_CMD_TEARDOWN:
 		if (!capable(CAP_SYS_ADMIN))
 			return -EPERM;
+		lock_kernel();
 		ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev));
+		unlock_kernel();
 		break;
 	case PKT_CTRL_CMD_STATUS:
+		lock_kernel();
 		pkt_get_status(&ctrl_cmd);
+		unlock_kernel();
 		break;
 	default:
 		return -ENOTTY;
@@ -3053,7 +3067,7 @@
 
 
 static const struct file_operations pkt_ctl_fops = {
-	.ioctl	 = pkt_ctl_ioctl,
+	.unlocked_ioctl	 = pkt_ctl_ioctl,
 	.owner	 = THIS_MODULE,
 };
 
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index f2fff57..9ae05c5 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -38,6 +38,7 @@
 #include <linux/interrupt.h>
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
+#include <linux/cdrom.h>
 #include <linux/module.h>
 
 #include <xen/xenbus.h>
@@ -153,6 +154,40 @@
 	return 0;
 }
 
+int blkif_ioctl(struct inode *inode, struct file *filep,
+		unsigned command, unsigned long argument)
+{
+	struct blkfront_info *info =
+		inode->i_bdev->bd_disk->private_data;
+	int i;
+
+	dev_dbg(&info->xbdev->dev, "command: 0x%x, argument: 0x%lx\n",
+		command, (long)argument);
+
+	switch (command) {
+	case CDROMMULTISESSION:
+		dev_dbg(&info->xbdev->dev, "FIXME: support multisession CDs later\n");
+		for (i = 0; i < sizeof(struct cdrom_multisession); i++)
+			if (put_user(0, (char __user *)(argument + i)))
+				return -EFAULT;
+		return 0;
+
+	case CDROM_GET_CAPABILITY: {
+		struct gendisk *gd = info->gd;
+		if (gd->flags & GENHD_FL_CD)
+			return 0;
+		return -EINVAL;
+	}
+
+	default:
+		/*printk(KERN_ALERT "ioctl %08x not supported by Xen blkdev\n",
+		  command);*/
+		return -EINVAL; /* same return as native Linux */
+	}
+
+	return 0;
+}
+
 /*
  * blkif_queue_request
  *
@@ -324,6 +359,9 @@
 	/* Make sure buffer addresses are sector-aligned. */
 	blk_queue_dma_alignment(rq, 511);
 
+	/* Make sure we don't use bounce buffers. */
+	blk_queue_bounce_limit(rq, BLK_BOUNCE_ANY);
+
 	gd->queue = rq;
 
 	return 0;
@@ -546,7 +584,7 @@
 
 	info->ring_ref = GRANT_INVALID_REF;
 
-	sring = (struct blkif_sring *)__get_free_page(GFP_KERNEL);
+	sring = (struct blkif_sring *)__get_free_page(GFP_NOIO | __GFP_HIGH);
 	if (!sring) {
 		xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
 		return -ENOMEM;
@@ -703,7 +741,8 @@
 	int j;
 
 	/* Stage 1: Make a safe copy of the shadow state. */
-	copy = kmalloc(sizeof(info->shadow), GFP_KERNEL);
+	copy = kmalloc(sizeof(info->shadow),
+		       GFP_NOIO | __GFP_REPEAT | __GFP_HIGH);
 	if (!copy)
 		return -ENOMEM;
 	memcpy(copy, info->shadow, sizeof(info->shadow));
@@ -959,7 +998,7 @@
 		struct xenbus_device *dev = info->xbdev;
 		enum xenbus_state state = xenbus_read_driver_state(dev->otherend);
 
-		if (state == XenbusStateClosing)
+		if (state == XenbusStateClosing && info->is_ready)
 			blkfront_closing(dev);
 	}
 	return 0;
@@ -971,6 +1010,7 @@
 	.open = blkif_open,
 	.release = blkif_release,
 	.getgeo = blkif_getgeo,
+	.ioctl = blkif_ioctl,
 };
 
 
@@ -1006,7 +1046,7 @@
 module_init(xlblk_init);
 
 
-static void xlblk_exit(void)
+static void __exit xlblk_exit(void)
 {
 	return xenbus_unregister_driver(&blkfront);
 }
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 69f26eb..a5da356 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -461,37 +461,27 @@
 			  struct media_event_desc *med)
 {
 	struct packet_command cgc;
-	unsigned char *buffer;
-	struct event_header *eh;
-	int ret = 1;
+	unsigned char buffer[8];
+	struct event_header *eh = (struct event_header *) buffer;
 
-	buffer = kmalloc(8, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	eh = (struct event_header *)buffer;
-
-	init_cdrom_command(&cgc, buffer, 8, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
 	cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
 	cgc.cmd[1] = 1;		/* IMMED */
 	cgc.cmd[4] = 1 << 4;	/* media event */
-	cgc.cmd[8] = 8;
+	cgc.cmd[8] = sizeof(buffer);
 	cgc.quiet = 1;
 
 	if (cdi->ops->generic_packet(cdi, &cgc))
-		goto err;
+		return 1;
 
 	if (be16_to_cpu(eh->data_len) < sizeof(*med))
-		goto err;
+		return 1;
 
 	if (eh->nea || eh->notification_class != 0x4)
-		goto err;
+		return 1;
 
-	memcpy(med, buffer + sizeof(*eh), sizeof(*med));
-	ret = 0;
-err:
-	kfree(buffer);
-	return ret;
+	memcpy(med, &buffer[sizeof(*eh)], sizeof(*med));
+	return 0;
 }
 
 /*
@@ -501,82 +491,68 @@
 static int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi)
 {
 	struct packet_command cgc;
-	char *buffer;
-	int ret = 1;
+	char buffer[16];
 
-	buffer = kmalloc(16, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
 
 	cgc.timeout = HZ;
 	cgc.quiet = 1;
 
 	if (!cdrom_mode_sense(cdi, &cgc, MRW_MODE_PC, 0)) {
 		cdi->mrw_mode_page = MRW_MODE_PC;
-		ret = 0;
+		return 0;
 	} else if (!cdrom_mode_sense(cdi, &cgc, MRW_MODE_PC_PRE1, 0)) {
 		cdi->mrw_mode_page = MRW_MODE_PC_PRE1;
-		ret = 0;
+		return 0;
 	}
-	kfree(buffer);
-	return ret;
+
+	return 1;
 }
 
 static int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write)
 {
 	struct packet_command cgc;
 	struct mrw_feature_desc *mfd;
-	unsigned char *buffer;
+	unsigned char buffer[16];
 	int ret;
 
 	*write = 0;
-	buffer = kmalloc(16, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
 
-	init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
 
 	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
 	cgc.cmd[3] = CDF_MRW;
-	cgc.cmd[8] = 16;
+	cgc.cmd[8] = sizeof(buffer);
 	cgc.quiet = 1;
 
 	if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	mfd = (struct mrw_feature_desc *)&buffer[sizeof(struct feature_header)];
-	if (be16_to_cpu(mfd->feature_code) != CDF_MRW) {
-		ret = 1;
-		goto err;
-	}
+	if (be16_to_cpu(mfd->feature_code) != CDF_MRW)
+		return 1;
 	*write = mfd->write;
 
 	if ((ret = cdrom_mrw_probe_pc(cdi))) {
 		*write = 0;
+		return ret;
 	}
-err:
-	kfree(buffer);
-	return ret;
+
+	return 0;
 }
 
 static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont)
 {
 	struct packet_command cgc;
-	unsigned char *buffer;
+	unsigned char buffer[12];
 	int ret;
 
 	printk(KERN_INFO "cdrom: %sstarting format\n", cont ? "Re" : "");
 
-	buffer = kmalloc(12, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
 	/*
 	 * FmtData bit set (bit 4), format type is 1
 	 */
-	init_cdrom_command(&cgc, buffer, 12, CGC_DATA_WRITE);
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_WRITE);
 	cgc.cmd[0] = GPCMD_FORMAT_UNIT;
 	cgc.cmd[1] = (1 << 4) | 1;
 
@@ -603,7 +579,6 @@
 	if (ret)
 		printk(KERN_INFO "cdrom: bgformat failed\n");
 
-	kfree(buffer);
 	return ret;
 }
 
@@ -663,17 +638,16 @@
 {
 	struct packet_command cgc;
 	struct mode_page_header *mph;
-	char *buffer;
+	char buffer[16];
 	int ret, offset, size;
 
-	buffer = kmalloc(16, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
 
-	init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ);
+	cgc.buffer = buffer;
+	cgc.buflen = sizeof(buffer);
 
 	if ((ret = cdrom_mode_sense(cdi, &cgc, cdi->mrw_mode_page, 0)))
-		goto err;
+		return ret;
 
 	mph = (struct mode_page_header *) buffer;
 	offset = be16_to_cpu(mph->desc_length);
@@ -683,70 +657,55 @@
 	cgc.buflen = size;
 
 	if ((ret = cdrom_mode_select(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	printk(KERN_INFO "cdrom: %s: mrw address space %s selected\n", cdi->name, mrw_address_space[space]);
-	ret = 0;
-err:
-	kfree(buffer);
-	return ret;
+	return 0;
 }
 
 static int cdrom_get_random_writable(struct cdrom_device_info *cdi,
 			      struct rwrt_feature_desc *rfd)
 {
 	struct packet_command cgc;
-	char *buffer;
+	char buffer[24];
 	int ret;
 
-	buffer = kmalloc(24, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	init_cdrom_command(&cgc, buffer, 24, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
 
 	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;	/* often 0x46 */
 	cgc.cmd[3] = CDF_RWRT;			/* often 0x0020 */
-	cgc.cmd[8] = 24;		        /* often 0x18 */
+	cgc.cmd[8] = sizeof(buffer);		/* often 0x18 */
 	cgc.quiet = 1;
 
 	if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	memcpy(rfd, &buffer[sizeof(struct feature_header)], sizeof (*rfd));
-	ret = 0;
-err:
-	kfree(buffer);
-	return ret;
+	return 0;
 }
 
 static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi)
 {
 	struct packet_command cgc;
-	char *buffer;
+	char buffer[16];
 	__be16 *feature_code;
 	int ret;
 
-	buffer = kmalloc(16, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
 
 	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
 	cgc.cmd[3] = CDF_HWDM;
-	cgc.cmd[8] = 16;
+	cgc.cmd[8] = sizeof(buffer);
 	cgc.quiet = 1;
 
 	if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	feature_code = (__be16 *) &buffer[sizeof(struct feature_header)];
 	if (be16_to_cpu(*feature_code) == CDF_HWDM)
-		ret = 0;
-err:
-	kfree(buffer);
-	return ret;
+		return 0;
+
+	return 1;
 }
 
 
@@ -837,14 +796,10 @@
 static int mo_open_write(struct cdrom_device_info *cdi)
 {
 	struct packet_command cgc;
-	char *buffer;
+	char buffer[255];
 	int ret;
 
-	buffer = kmalloc(255, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	init_cdrom_command(&cgc, buffer, 4, CGC_DATA_READ);
+	init_cdrom_command(&cgc, &buffer, 4, CGC_DATA_READ);
 	cgc.quiet = 1;
 
 	/*
@@ -861,15 +816,10 @@
 	}
 
 	/* drive gave us no info, let the user go ahead */
-	if (ret) {
-		ret = 0;
-		goto err;
-	}
+	if (ret)
+		return 0;
 
-	ret = buffer[3] & 0x80;
-err:
-	kfree(buffer);
-	return ret;
+	return buffer[3] & 0x80;
 }
 
 static int cdrom_ram_open_write(struct cdrom_device_info *cdi)
@@ -892,19 +842,15 @@
 static void cdrom_mmc3_profile(struct cdrom_device_info *cdi)
 {
 	struct packet_command cgc;
-	char *buffer;
+	char buffer[32];
 	int ret, mmc3_profile;
 
-	buffer = kmalloc(32, GFP_KERNEL);
-	if (!buffer)
-		return;
-
-	init_cdrom_command(&cgc, buffer, 32, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ);
 
 	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
 	cgc.cmd[1] = 0;
 	cgc.cmd[2] = cgc.cmd[3] = 0;		/* Starting Feature Number */
-	cgc.cmd[8] = 32;		        /* Allocation Length */
+	cgc.cmd[8] = sizeof(buffer);		/* Allocation Length */
 	cgc.quiet = 1;
 
 	if ((ret = cdi->ops->generic_packet(cdi, &cgc)))
@@ -913,7 +859,6 @@
 		mmc3_profile = (buffer[6] << 8) | buffer[7];
 
 	cdi->mmc3_profile = mmc3_profile;
-	kfree(buffer);
 }
 
 static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi)
@@ -1628,15 +1573,12 @@
 static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
 {
 	int ret;
-	u_char *buf;
+	u_char buf[20];
 	struct packet_command cgc;
 	struct cdrom_device_ops *cdo = cdi->ops;
-	rpc_state_t *rpc_state;
+	rpc_state_t rpc_state;
 
-	buf = kzalloc(20, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
+	memset(buf, 0, sizeof(buf));
 	init_cdrom_command(&cgc, buf, 0, CGC_DATA_READ);
 
 	switch (ai->type) {
@@ -1647,7 +1589,7 @@
 		setup_report_key(&cgc, ai->lsa.agid, 0);
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 
 		ai->lsa.agid = buf[7] >> 6;
 		/* Returning data, let host change state */
@@ -1658,7 +1600,7 @@
 		setup_report_key(&cgc, ai->lsk.agid, 2);
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 
 		copy_key(ai->lsk.key, &buf[4]);
 		/* Returning data, let host change state */
@@ -1669,7 +1611,7 @@
 		setup_report_key(&cgc, ai->lsc.agid, 1);
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 
 		copy_chal(ai->lsc.chal, &buf[4]);
 		/* Returning data, let host change state */
@@ -1686,7 +1628,7 @@
 		cgc.cmd[2] = ai->lstk.lba >> 24;
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 
 		ai->lstk.cpm = (buf[4] >> 7) & 1;
 		ai->lstk.cp_sec = (buf[4] >> 6) & 1;
@@ -1700,7 +1642,7 @@
 		setup_report_key(&cgc, ai->lsasf.agid, 5);
 		
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 
 		ai->lsasf.asf = buf[7] & 1;
 		break;
@@ -1713,7 +1655,7 @@
 		copy_chal(&buf[4], ai->hsc.chal);
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 
 		ai->type = DVD_LU_SEND_KEY1;
 		break;
@@ -1726,7 +1668,7 @@
 
 		if ((ret = cdo->generic_packet(cdi, &cgc))) {
 			ai->type = DVD_AUTH_FAILURE;
-			goto err;
+			return ret;
 		}
 		ai->type = DVD_AUTH_ESTABLISHED;
 		break;
@@ -1737,23 +1679,24 @@
 		cdinfo(CD_DVD, "entering DVD_INVALIDATE_AGID\n"); 
 		setup_report_key(&cgc, ai->lsa.agid, 0x3f);
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 		break;
 
 	/* Get region settings */
 	case DVD_LU_SEND_RPC_STATE:
 		cdinfo(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n");
 		setup_report_key(&cgc, 0, 8);
+		memset(&rpc_state, 0, sizeof(rpc_state_t));
+		cgc.buffer = (char *) &rpc_state;
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 
-		rpc_state = (rpc_state_t *)buf;
-		ai->lrpcs.type = rpc_state->type_code;
-		ai->lrpcs.vra = rpc_state->vra;
-		ai->lrpcs.ucca = rpc_state->ucca;
-		ai->lrpcs.region_mask = rpc_state->region_mask;
-		ai->lrpcs.rpc_scheme = rpc_state->rpc_scheme;
+		ai->lrpcs.type = rpc_state.type_code;
+		ai->lrpcs.vra = rpc_state.vra;
+		ai->lrpcs.ucca = rpc_state.ucca;
+		ai->lrpcs.region_mask = rpc_state.region_mask;
+		ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme;
 		break;
 
 	/* Set region settings */
@@ -1764,23 +1707,20 @@
 		buf[4] = ai->hrpcs.pdrc;
 
 		if ((ret = cdo->generic_packet(cdi, &cgc)))
-			goto err;
+			return ret;
 		break;
 
 	default:
 		cdinfo(CD_WARNING, "Invalid DVD key ioctl (%d)\n", ai->type);
-		ret = -ENOTTY;
-		goto err;
+		return -ENOTTY;
 	}
-	ret = 0;
-err:
-	kfree(buf);
-	return ret;
+
+	return 0;
 }
 
 static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
 {
-	unsigned char *buf, *base;
+	unsigned char buf[21], *base;
 	struct dvd_layer *layer;
 	struct packet_command cgc;
 	struct cdrom_device_ops *cdo = cdi->ops;
@@ -1789,11 +1729,7 @@
 	if (layer_num >= DVD_LAYERS)
 		return -EINVAL;
 
-	buf = kmalloc(21, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	init_cdrom_command(&cgc, buf, 21, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[6] = layer_num;
 	cgc.cmd[7] = s->type;
@@ -1805,7 +1741,7 @@
 	cgc.quiet = 1;
 
 	if ((ret = cdo->generic_packet(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	base = &buf[4];
 	layer = &s->physical.layer[layer_num];
@@ -1829,24 +1765,17 @@
 	layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
 	layer->bca = base[16] >> 7;
 
-	ret = 0;
-err:
-	kfree(buf);
-	return ret;
+	return 0;
 }
 
 static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s)
 {
 	int ret;
-	u_char *buf;
+	u_char buf[8];
 	struct packet_command cgc;
 	struct cdrom_device_ops *cdo = cdi->ops;
 
-	buf = kmalloc(8, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	init_cdrom_command(&cgc, buf, 8, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[6] = s->copyright.layer_num;
 	cgc.cmd[7] = s->type;
@@ -1854,15 +1783,12 @@
 	cgc.cmd[9] = cgc.buflen & 0xff;
 
 	if ((ret = cdo->generic_packet(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	s->copyright.cpst = buf[4];
 	s->copyright.rmi = buf[5];
 
-	ret = 0;
-err:
-	kfree(buf);
-	return ret;
+	return 0;
 }
 
 static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s)
@@ -1894,33 +1820,26 @@
 static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s)
 {
 	int ret;
-	u_char *buf;
+	u_char buf[4 + 188];
 	struct packet_command cgc;
 	struct cdrom_device_ops *cdo = cdi->ops;
 
-	buf = kmalloc(4 + 188, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	init_cdrom_command(&cgc, buf, 4 + 188, CGC_DATA_READ);
+	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
 	cgc.cmd[7] = s->type;
 	cgc.cmd[9] = cgc.buflen & 0xff;
 
 	if ((ret = cdo->generic_packet(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	s->bca.len = buf[0] << 8 | buf[1];
 	if (s->bca.len < 12 || s->bca.len > 188) {
 		cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len);
-		ret = -EIO;
-		goto err;
+		return -EIO;
 	}
 	memcpy(s->bca.value, &buf[4], s->bca.len);
-	ret = 0;
-err:
-	kfree(buf);
-	return ret;
+
+	return 0;
 }
 
 static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
@@ -2020,13 +1939,9 @@
 {
 	struct cdrom_device_ops *cdo = cdi->ops;
 	struct packet_command cgc;
-	char *buffer;
+	char buffer[32];
 	int ret;
 
-	buffer = kmalloc(32, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
 	init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ);
 	cgc.cmd[0] = GPCMD_READ_SUBCHANNEL;
 	cgc.cmd[1] = 2;     /* MSF addressing */
@@ -2035,7 +1950,7 @@
 	cgc.cmd[8] = 16;
 
 	if ((ret = cdo->generic_packet(cdi, &cgc)))
-		goto err;
+		return ret;
 
 	subchnl->cdsc_audiostatus = cgc.buffer[1];
 	subchnl->cdsc_format = CDROM_MSF;
@@ -2050,10 +1965,7 @@
 	subchnl->cdsc_absaddr.msf.second = cgc.buffer[10];
 	subchnl->cdsc_absaddr.msf.frame = cgc.buffer[11];
 
-	ret = 0;
-err:
-	kfree(buffer);
-	return ret;
+	return 0;
 }
 
 /*
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 4c1c584..81630a6 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -101,7 +101,6 @@
 
 obj-$(CONFIG_MWAVE)		+= mwave/
 obj-$(CONFIG_AGP)		+= agp/
-obj-$(CONFIG_DRM)		+= drm/
 obj-$(CONFIG_PCMCIA)		+= pcmcia/
 obj-$(CONFIG_IPMI_HANDLER)	+= ipmi/
 
diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile
deleted file mode 100644
index 1283ded..0000000
--- a/drivers/char/drm/Makefile
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Makefile for the drm device driver.  This driver provides support for the
-# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
-
-drm-objs    :=	drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
-		drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \
-		drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
-		drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
-		drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o
-
-tdfx-objs   := tdfx_drv.o
-r128-objs   := r128_drv.o r128_cce.o r128_state.o r128_irq.o
-mga-objs    := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
-i810-objs   := i810_drv.o i810_dma.o
-i830-objs   := i830_drv.o i830_dma.o i830_irq.o
-i915-objs   := i915_drv.o i915_dma.o i915_irq.o i915_mem.o
-radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o
-sis-objs    := sis_drv.o sis_mm.o
-savage-objs := savage_drv.o savage_bci.o savage_state.o
-via-objs    := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o
-
-ifeq ($(CONFIG_COMPAT),y)
-drm-objs    += drm_ioc32.o
-radeon-objs += radeon_ioc32.o
-mga-objs    += mga_ioc32.o
-r128-objs   += r128_ioc32.o
-i915-objs   += i915_ioc32.o
-endif
-
-obj-$(CONFIG_DRM)	+= drm.o
-obj-$(CONFIG_DRM_TDFX)	+= tdfx.o
-obj-$(CONFIG_DRM_R128)	+= r128.o
-obj-$(CONFIG_DRM_RADEON)+= radeon.o
-obj-$(CONFIG_DRM_MGA)	+= mga.o
-obj-$(CONFIG_DRM_I810)	+= i810.o
-obj-$(CONFIG_DRM_I830)	+= i830.o
-obj-$(CONFIG_DRM_I915)  += i915.o
-obj-$(CONFIG_DRM_SIS)   += sis.o
-obj-$(CONFIG_DRM_SAVAGE)+= savage.o
-obj-$(CONFIG_DRM_VIA)	+=via.o
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
index 4a933d4..59ca351 100644
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -32,8 +32,9 @@
 #include <linux/fs.h>
 #include <linux/delay.h>
 #include <linux/bitrev.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
+#include <linux/smp_lock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
@@ -1405,11 +1406,11 @@
 	DEBUGP(3, dev, "<- stop_monitor\n");
 }
 
-static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
-		     unsigned long arg)
+static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct cm4000_dev *dev = filp->private_data;
 	unsigned int iobase = dev->p_dev->io.BasePort1;
+	struct inode *inode = filp->f_path.dentry->d_inode;
 	struct pcmcia_device *link;
 	int size;
 	int rc;
@@ -1426,38 +1427,42 @@
 	DEBUGP(3, dev, "cmm_ioctl(device=%d.%d) %s\n", imajor(inode),
 	       iminor(inode), ioctl_names[_IOC_NR(cmd)]);
 
+	lock_kernel();
+	rc = -ENODEV;
 	link = dev_table[iminor(inode)];
 	if (!pcmcia_dev_present(link)) {
 		DEBUGP(4, dev, "DEV_OK false\n");
-		return -ENODEV;
+		goto out;
 	}
 
 	if (test_bit(IS_CMM_ABSENT, &dev->flags)) {
 		DEBUGP(4, dev, "CMM_ABSENT flag set\n");
-		return -ENODEV;
+		goto out;
 	}
+	rc = EINVAL;
 
 	if (_IOC_TYPE(cmd) != CM_IOC_MAGIC) {
 		DEBUGP(4, dev, "ioctype mismatch\n");
-		return -EINVAL;
+		goto out;
 	}
 	if (_IOC_NR(cmd) > CM_IOC_MAXNR) {
 		DEBUGP(4, dev, "iocnr mismatch\n");
-		return -EINVAL;
+		goto out;
 	}
 	size = _IOC_SIZE(cmd);
-	rc = 0;
+	rc = -EFAULT;
 	DEBUGP(4, dev, "iocdir=%.4x iocr=%.4x iocw=%.4x iocsize=%d cmd=%.4x\n",
 	      _IOC_DIR(cmd), _IOC_READ, _IOC_WRITE, size, cmd);
 
 	if (_IOC_DIR(cmd) & _IOC_READ) {
 		if (!access_ok(VERIFY_WRITE, argp, size))
-			return -EFAULT;
+			goto out;
 	}
 	if (_IOC_DIR(cmd) & _IOC_WRITE) {
 		if (!access_ok(VERIFY_READ, argp, size))
-			return -EFAULT;
+			goto out;
 	}
+	rc = 0;
 
 	switch (cmd) {
 	case CM_IOCGSTATUS:
@@ -1477,9 +1482,9 @@
 			if (test_bit(IS_BAD_CARD, &dev->flags))
 				status |= CM_BAD_CARD;
 			if (copy_to_user(argp, &status, sizeof(int)))
-				return -EFAULT;
+				rc = -EFAULT;
 		}
-		return 0;
+		break;
 	case CM_IOCGATR:
 		DEBUGP(4, dev, "... in CM_IOCGATR\n");
 		{
@@ -1492,25 +1497,29 @@
 			      || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
 				  != 0)))) {
 				if (filp->f_flags & O_NONBLOCK)
-					return -EAGAIN;
-				return -ERESTARTSYS;
+					rc = -EAGAIN;
+				else
+					rc = -ERESTARTSYS;
+				break;
 			}
 
+			rc = -EFAULT;
 			if (test_bit(IS_ATR_VALID, &dev->flags) == 0) {
 				tmp = -1;
 				if (copy_to_user(&(atreq->atr_len), &tmp,
 						 sizeof(int)))
-					return -EFAULT;
+					break;
 			} else {
 				if (copy_to_user(atreq->atr, dev->atr,
 						 dev->atr_len))
-					return -EFAULT;
+					break;
 
 				tmp = dev->atr_len;
 				if (copy_to_user(&(atreq->atr_len), &tmp, sizeof(int)))
-					return -EFAULT;
+					break;
 			}
-			return 0;
+			rc = 0;
+			break;
 		}
 	case CM_IOCARDOFF:
 
@@ -1538,8 +1547,10 @@
 			      || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
 				  == 0)))) {
 				if (filp->f_flags & O_NONBLOCK)
-					return -EAGAIN;
-				return -ERESTARTSYS;
+					rc = -EAGAIN;
+				else
+					rc = -ERESTARTSYS;
+				break;
 			}
 			/* Set Flags0 = 0x42 */
 			DEBUGP(4, dev, "Set Flags0=0x42 \n");
@@ -1554,8 +1565,10 @@
 			      || (test_bit(IS_ATR_VALID, (void *)&dev->flags) !=
 				  0)))) {
 				if (filp->f_flags & O_NONBLOCK)
-					return -EAGAIN;
-				return -ERESTARTSYS;
+					rc = -EAGAIN;
+				else
+					rc = -ERESTARTSYS;
+				break;
 			}
 		}
 		/* release lock */
@@ -1568,8 +1581,10 @@
 			struct ptsreq krnptsreq;
 
 			if (copy_from_user(&krnptsreq, argp,
-					   sizeof(struct ptsreq)))
-				return -EFAULT;
+					   sizeof(struct ptsreq))) {
+				rc = -EFAULT;
+				break;
+			}
 
 			rc = 0;
 			DEBUGP(4, dev, "... in CM_IOCSPTS\n");
@@ -1580,8 +1595,10 @@
 			      || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
 				  != 0)))) {
 				if (filp->f_flags & O_NONBLOCK)
-					return -EAGAIN;
-				return -ERESTARTSYS;
+					rc = -EAGAIN;
+				else
+					rc = -ERESTARTSYS;
+				break;
 			}
 			/* get IO lock */
 			if (wait_event_interruptible
@@ -1590,8 +1607,10 @@
 			      || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
 				  == 0)))) {
 				if (filp->f_flags & O_NONBLOCK)
-					return -EAGAIN;
-				return -ERESTARTSYS;
+					rc = -EAGAIN;
+				else
+					rc = -ERESTARTSYS;
+				break;
 			}
 
 			if ((rc = set_protocol(dev, &krnptsreq)) != 0) {
@@ -1604,7 +1623,7 @@
 			wake_up_interruptible(&dev->ioq);
 
 		}
-		return rc;
+		break;
 #ifdef PCMCIA_DEBUG
 	case CM_IOSDBGLVL:	/* set debug log level */
 		{
@@ -1612,18 +1631,20 @@
 
 			old_pc_debug = pc_debug;
 			if (copy_from_user(&pc_debug, argp, sizeof(int)))
-				return -EFAULT;
-
-			if (old_pc_debug != pc_debug)
+				rc = -EFAULT;
+			else if (old_pc_debug != pc_debug)
 				DEBUGP(0, dev, "Changed debug log level "
 				       "to %i\n", pc_debug);
 		}
-		return rc;
+		break;
 #endif
 	default:
 		DEBUGP(4, dev, "... in default (unknown IOCTL code)\n");
-		return -EINVAL;
+		rc = -ENOTTY;
 	}
+out:
+	unlock_kernel();
+	return rc;
 }
 
 static int cmm_open(struct inode *inode, struct file *filp)
@@ -1631,16 +1652,22 @@
 	struct cm4000_dev *dev;
 	struct pcmcia_device *link;
 	int minor = iminor(inode);
+	int ret;
 
 	if (minor >= CM4000_MAX_DEV)
 		return -ENODEV;
 
+	lock_kernel();
 	link = dev_table[minor];
-	if (link == NULL || !pcmcia_dev_present(link))
-		return -ENODEV;
+	if (link == NULL || !pcmcia_dev_present(link)) {
+		ret = -ENODEV;
+		goto out;
+	}
 
-	if (link->open)
-		return -EBUSY;
+	if (link->open) {
+		ret = -EBUSY;
+		goto out;
+	}
 
 	dev = link->priv;
 	filp->private_data = dev;
@@ -1660,8 +1687,10 @@
 	 * vaild = block until valid (or card
 	 * inserted)
 	 */
-	if (filp->f_flags & O_NONBLOCK)
-		return -EAGAIN;
+	if (filp->f_flags & O_NONBLOCK) {
+		ret = -EAGAIN;
+		goto out;
+	}
 
 	dev->mdelay = T_50MSEC;
 
@@ -1671,7 +1700,10 @@
 	link->open = 1;		/* only one open per device */
 
 	DEBUGP(2, dev, "<- cmm_open\n");
-	return nonseekable_open(inode, filp);
+	ret = nonseekable_open(inode, filp);
+out:
+	unlock_kernel();
+	return ret;
 }
 
 static int cmm_close(struct inode *inode, struct file *filp)
@@ -1897,7 +1929,7 @@
 	.owner	= THIS_MODULE,
 	.read	= cmm_read,
 	.write	= cmm_write,
-	.ioctl	= cmm_ioctl,
+	.unlocked_ioctl	= cmm_ioctl,
 	.open	= cmm_open,
 	.release= cmm_close,
 };
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
index 035084c..6181f8a 100644
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -26,6 +26,7 @@
 #include <linux/fs.h>
 #include <linux/delay.h>
 #include <linux/poll.h>
+#include <linux/smp_lock.h>
 #include <linux/wait.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -448,23 +449,30 @@
 	struct reader_dev *dev;
 	struct pcmcia_device *link;
 	int minor = iminor(inode);
+	int ret;
 
 	if (minor >= CM_MAX_DEV)
 		return -ENODEV;
 
+	lock_kernel();
 	link = dev_table[minor];
-	if (link == NULL || !pcmcia_dev_present(link))
-		return -ENODEV;
+	if (link == NULL || !pcmcia_dev_present(link)) {
+		ret = -ENODEV;
+		goto out;
+	}
 
-	if (link->open)
-		return -EBUSY;
+	if (link->open) {
+		ret = -EBUSY;
+		goto out;
+	}
 
 	dev = link->priv;
 	filp->private_data = dev;
 
 	if (filp->f_flags & O_NONBLOCK) {
 		DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
-		return -EAGAIN;
+		ret = -EAGAIN;
+		goto out;
 	}
 
 	link->open = 1;
@@ -473,7 +481,10 @@
 	mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
 
 	DEBUGP(2, dev, "<- cm4040_open (successfully)\n");
-	return nonseekable_open(inode, filp);
+	ret = nonseekable_open(inode, filp);
+out:
+	unlock_kernel();
+	return ret;
 }
 
 static int cm4040_close(struct inode *inode, struct file *filp)
diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c
index 00c7f84..cc7dcea 100644
--- a/drivers/char/pcmcia/ipwireless/main.c
+++ b/drivers/char/pcmcia/ipwireless/main.c
@@ -28,7 +28,6 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 
-#include <pcmcia/version.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/device_id.h>
 #include <pcmcia/ss.h>
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 43b71b6..e522144 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -174,4 +174,30 @@
 	  Select this option if you want to enable the random number generator
 	  on the HIFN 795x crypto adapters.
 
+config CRYPTO_DEV_TALITOS
+	tristate "Talitos Freescale Security Engine (SEC)"
+	select CRYPTO_ALGAPI
+	select CRYPTO_AUTHENC
+	select HW_RANDOM
+	depends on FSL_SOC
+	help
+	  Say 'Y' here to use the Freescale Security Engine (SEC)
+	  to offload cryptographic algorithm computation.
+
+	  The Freescale SEC is present on PowerQUICC 'E' processors, such
+	  as the MPC8349E and MPC8548E.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called talitos.
+
+config CRYPTO_DEV_IXP4XX
+	tristate "Driver for IXP4xx crypto hardware acceleration"
+	depends on ARCH_IXP4XX
+	select CRYPTO_DES
+	select CRYPTO_ALGAPI
+	select CRYPTO_AUTHENC
+	select CRYPTO_BLKCIPHER
+	help
+	  Driver for the IXP4xx NPE crypto engine.
+
 endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index c0327f0..73557b2 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -2,3 +2,5 @@
 obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o
 obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
 obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
+obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
+obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index 81f3f95..4d22b21 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -29,7 +29,6 @@
 #include <linux/dma-mapping.h>
 #include <linux/scatterlist.h>
 #include <linux/highmem.h>
-#include <linux/interrupt.h>
 #include <linux/crypto.h>
 #include <linux/hw_random.h>
 #include <linux/ktime.h>
@@ -369,7 +368,9 @@
 #define	HIFN_D_DST_RSIZE		80*4
 #define	HIFN_D_RES_RSIZE		24*4
 
-#define HIFN_QUEUE_LENGTH		HIFN_D_CMD_RSIZE-5
+#define HIFN_D_DST_DALIGN		4
+
+#define HIFN_QUEUE_LENGTH		HIFN_D_CMD_RSIZE-1
 
 #define AES_MIN_KEY_SIZE		16
 #define AES_MAX_KEY_SIZE		32
@@ -535,10 +536,10 @@
  */
 struct hifn_mac_command
 {
-	volatile u16 		masks;
-	volatile u16 		header_skip;
-	volatile u16 		source_count;
-	volatile u16 		reserved;
+	volatile __le16 	masks;
+	volatile __le16 	header_skip;
+	volatile __le16 	source_count;
+	volatile __le16 	reserved;
 };
 
 #define	HIFN_MAC_CMD_ALG_MASK		0x0001
@@ -564,10 +565,10 @@
 
 struct hifn_comp_command
 {
-	volatile u16 		masks;
-	volatile u16 		header_skip;
-	volatile u16 		source_count;
-	volatile u16 		reserved;
+	volatile __le16 	masks;
+	volatile __le16 	header_skip;
+	volatile __le16 	source_count;
+	volatile __le16 	reserved;
 };
 
 #define	HIFN_COMP_CMD_SRCLEN_M		0xc000
@@ -583,10 +584,10 @@
 
 struct hifn_base_result
 {
-	volatile u16 		flags;
-	volatile u16 		session;
-	volatile u16 		src_cnt;		/* 15:0 of source count */
-	volatile u16 		dst_cnt;		/* 15:0 of dest count */
+	volatile __le16 	flags;
+	volatile __le16 	session;
+	volatile __le16 	src_cnt;		/* 15:0 of source count */
+	volatile __le16 	dst_cnt;		/* 15:0 of dest count */
 };
 
 #define	HIFN_BASE_RES_DSTOVERRUN	0x0200	/* destination overrun */
@@ -597,8 +598,8 @@
 
 struct hifn_comp_result
 {
-	volatile u16 		flags;
-	volatile u16 		crc;
+	volatile __le16		flags;
+	volatile __le16		crc;
 };
 
 #define	HIFN_COMP_RES_LCB_M		0xff00	/* longitudinal check byte */
@@ -609,8 +610,8 @@
 
 struct hifn_mac_result
 {
-	volatile u16 		flags;
-	volatile u16 		reserved;
+	volatile __le16 	flags;
+	volatile __le16 	reserved;
 	/* followed by 0, 6, 8, or 10 u16's of the MAC, then crypt */
 };
 
@@ -619,8 +620,8 @@
 
 struct hifn_crypt_result
 {
-	volatile u16 		flags;
-	volatile u16 		reserved;
+	volatile __le16		flags;
+	volatile __le16		reserved;
 };
 
 #define	HIFN_CRYPT_RES_SRC_NOTZERO	0x0001	/* source expired */
@@ -686,12 +687,12 @@
 
 static inline void hifn_write_0(struct hifn_device *dev, u32 reg, u32 val)
 {
-	writel(val, dev->bar[0] + reg);
+	writel((__force u32)cpu_to_le32(val), dev->bar[0] + reg);
 }
 
 static inline void hifn_write_1(struct hifn_device *dev, u32 reg, u32 val)
 {
-	writel(val, dev->bar[1] + reg);
+	writel((__force u32)cpu_to_le32(val), dev->bar[1] + reg);
 }
 
 static void hifn_wait_puc(struct hifn_device *dev)
@@ -894,7 +895,7 @@
 	char *offtbl = NULL;
 	int i;
 
-	for (i = 0; i < sizeof(pci2id)/sizeof(pci2id[0]); i++) {
+	for (i = 0; i < ARRAY_SIZE(pci2id); i++) {
 		if (pci2id[i].pci_vendor == dev->pdev->vendor &&
 				pci2id[i].pci_prod == dev->pdev->device) {
 			offtbl = pci2id[i].card_id;
@@ -1037,14 +1038,14 @@
 	hifn_write_0(dev, HIFN_0_PUIER, HIFN_PUIER_DSTOVER);
 
 	/* write all 4 ring address registers */
-	hifn_write_1(dev, HIFN_1_DMA_CRAR, __cpu_to_le32(dptr +
-				offsetof(struct hifn_dma, cmdr[0])));
-	hifn_write_1(dev, HIFN_1_DMA_SRAR, __cpu_to_le32(dptr +
-				offsetof(struct hifn_dma, srcr[0])));
-	hifn_write_1(dev, HIFN_1_DMA_DRAR, __cpu_to_le32(dptr +
-				offsetof(struct hifn_dma, dstr[0])));
-	hifn_write_1(dev, HIFN_1_DMA_RRAR, __cpu_to_le32(dptr +
-				offsetof(struct hifn_dma, resr[0])));
+	hifn_write_1(dev, HIFN_1_DMA_CRAR, dptr +
+				offsetof(struct hifn_dma, cmdr[0]));
+	hifn_write_1(dev, HIFN_1_DMA_SRAR, dptr +
+				offsetof(struct hifn_dma, srcr[0]));
+	hifn_write_1(dev, HIFN_1_DMA_DRAR, dptr +
+				offsetof(struct hifn_dma, dstr[0]));
+	hifn_write_1(dev, HIFN_1_DMA_RRAR, dptr +
+				offsetof(struct hifn_dma, resr[0]));
 
 	mdelay(2);
 #if 0
@@ -1166,109 +1167,15 @@
 	return cmd_len;
 }
 
-static int hifn_setup_src_desc(struct hifn_device *dev, struct page *page,
-		unsigned int offset, unsigned int size)
-{
-	struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
-	int idx;
-	dma_addr_t addr;
-
-	addr = pci_map_page(dev->pdev, page, offset, size, PCI_DMA_TODEVICE);
-
-	idx = dma->srci;
-
-	dma->srcr[idx].p = __cpu_to_le32(addr);
-	dma->srcr[idx].l = __cpu_to_le32(size) | HIFN_D_VALID |
-			HIFN_D_MASKDONEIRQ | HIFN_D_NOINVALID | HIFN_D_LAST;
-
-	if (++idx == HIFN_D_SRC_RSIZE) {
-		dma->srcr[idx].l = __cpu_to_le32(HIFN_D_VALID |
-				HIFN_D_JUMP |
-				HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
-		idx = 0;
-	}
-
-	dma->srci = idx;
-	dma->srcu++;
-
-	if (!(dev->flags & HIFN_FLAG_SRC_BUSY)) {
-		hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_S_CTRL_ENA);
-		dev->flags |= HIFN_FLAG_SRC_BUSY;
-	}
-
-	return size;
-}
-
-static void hifn_setup_res_desc(struct hifn_device *dev)
-{
-	struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
-
-	dma->resr[dma->resi].l = __cpu_to_le32(HIFN_USED_RESULT |
-			HIFN_D_VALID | HIFN_D_LAST);
-	/*
-	 * dma->resr[dma->resi].l = __cpu_to_le32(HIFN_MAX_RESULT | HIFN_D_VALID |
-	 *					HIFN_D_LAST | HIFN_D_NOINVALID);
-	 */
-
-	if (++dma->resi == HIFN_D_RES_RSIZE) {
-		dma->resr[HIFN_D_RES_RSIZE].l = __cpu_to_le32(HIFN_D_VALID |
-				HIFN_D_JUMP | HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
-		dma->resi = 0;
-	}
-
-	dma->resu++;
-
-	if (!(dev->flags & HIFN_FLAG_RES_BUSY)) {
-		hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_R_CTRL_ENA);
-		dev->flags |= HIFN_FLAG_RES_BUSY;
-	}
-}
-
-static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
-		unsigned offset, unsigned size)
-{
-	struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
-	int idx;
-	dma_addr_t addr;
-
-	addr = pci_map_page(dev->pdev, page, offset, size, PCI_DMA_FROMDEVICE);
-
-	idx = dma->dsti;
-	dma->dstr[idx].p = __cpu_to_le32(addr);
-	dma->dstr[idx].l = __cpu_to_le32(size |	HIFN_D_VALID |
-			HIFN_D_MASKDONEIRQ | HIFN_D_NOINVALID | HIFN_D_LAST);
-
-	if (++idx == HIFN_D_DST_RSIZE) {
-		dma->dstr[idx].l = __cpu_to_le32(HIFN_D_VALID |
-				HIFN_D_JUMP | HIFN_D_MASKDONEIRQ |
-				HIFN_D_LAST | HIFN_D_NOINVALID);
-		idx = 0;
-	}
-	dma->dsti = idx;
-	dma->dstu++;
-
-	if (!(dev->flags & HIFN_FLAG_DST_BUSY)) {
-		hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_D_CTRL_ENA);
-		dev->flags |= HIFN_FLAG_DST_BUSY;
-	}
-}
-
-static int hifn_setup_dma(struct hifn_device *dev, struct page *spage, unsigned int soff,
-		struct page *dpage, unsigned int doff, unsigned int nbytes, void *priv,
-		struct hifn_context *ctx)
+static int hifn_setup_cmd_desc(struct hifn_device *dev,
+		struct hifn_context *ctx, void *priv, unsigned int nbytes)
 {
 	struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
 	int cmd_len, sa_idx;
 	u8 *buf, *buf_pos;
 	u16 mask;
 
-	dprintk("%s: spage: %p, soffset: %u, dpage: %p, doffset: %u, nbytes: %u, priv: %p, ctx: %p.\n",
-			dev->name, spage, soff, dpage, doff, nbytes, priv, ctx);
-
-	sa_idx = dma->resi;
-
-	hifn_setup_src_desc(dev, spage, soff, nbytes);
-
+	sa_idx = dma->cmdi;
 	buf_pos = buf = dma->command_bufs[dma->cmdi];
 
 	mask = 0;
@@ -1370,16 +1277,113 @@
 		hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_C_CTRL_ENA);
 		dev->flags |= HIFN_FLAG_CMD_BUSY;
 	}
-
-	hifn_setup_dst_desc(dev, dpage, doff, nbytes);
-	hifn_setup_res_desc(dev);
-
 	return 0;
 
 err_out:
 	return -EINVAL;
 }
 
+static int hifn_setup_src_desc(struct hifn_device *dev, struct page *page,
+		unsigned int offset, unsigned int size)
+{
+	struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+	int idx;
+	dma_addr_t addr;
+
+	addr = pci_map_page(dev->pdev, page, offset, size, PCI_DMA_TODEVICE);
+
+	idx = dma->srci;
+
+	dma->srcr[idx].p = __cpu_to_le32(addr);
+	dma->srcr[idx].l = __cpu_to_le32(size | HIFN_D_VALID |
+			HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+
+	if (++idx == HIFN_D_SRC_RSIZE) {
+		dma->srcr[idx].l = __cpu_to_le32(HIFN_D_VALID |
+				HIFN_D_JUMP |
+				HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+		idx = 0;
+	}
+
+	dma->srci = idx;
+	dma->srcu++;
+
+	if (!(dev->flags & HIFN_FLAG_SRC_BUSY)) {
+		hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_S_CTRL_ENA);
+		dev->flags |= HIFN_FLAG_SRC_BUSY;
+	}
+
+	return size;
+}
+
+static void hifn_setup_res_desc(struct hifn_device *dev)
+{
+	struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+
+	dma->resr[dma->resi].l = __cpu_to_le32(HIFN_USED_RESULT |
+			HIFN_D_VALID | HIFN_D_LAST);
+	/*
+	 * dma->resr[dma->resi].l = __cpu_to_le32(HIFN_MAX_RESULT | HIFN_D_VALID |
+	 *					HIFN_D_LAST);
+	 */
+
+	if (++dma->resi == HIFN_D_RES_RSIZE) {
+		dma->resr[HIFN_D_RES_RSIZE].l = __cpu_to_le32(HIFN_D_VALID |
+				HIFN_D_JUMP | HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+		dma->resi = 0;
+	}
+
+	dma->resu++;
+
+	if (!(dev->flags & HIFN_FLAG_RES_BUSY)) {
+		hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_R_CTRL_ENA);
+		dev->flags |= HIFN_FLAG_RES_BUSY;
+	}
+}
+
+static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
+		unsigned offset, unsigned size)
+{
+	struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+	int idx;
+	dma_addr_t addr;
+
+	addr = pci_map_page(dev->pdev, page, offset, size, PCI_DMA_FROMDEVICE);
+
+	idx = dma->dsti;
+	dma->dstr[idx].p = __cpu_to_le32(addr);
+	dma->dstr[idx].l = __cpu_to_le32(size |	HIFN_D_VALID |
+			HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+
+	if (++idx == HIFN_D_DST_RSIZE) {
+		dma->dstr[idx].l = __cpu_to_le32(HIFN_D_VALID |
+				HIFN_D_JUMP | HIFN_D_MASKDONEIRQ |
+				HIFN_D_LAST);
+		idx = 0;
+	}
+	dma->dsti = idx;
+	dma->dstu++;
+
+	if (!(dev->flags & HIFN_FLAG_DST_BUSY)) {
+		hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_D_CTRL_ENA);
+		dev->flags |= HIFN_FLAG_DST_BUSY;
+	}
+}
+
+static int hifn_setup_dma(struct hifn_device *dev, struct page *spage, unsigned int soff,
+		struct page *dpage, unsigned int doff, unsigned int nbytes, void *priv,
+		struct hifn_context *ctx)
+{
+	dprintk("%s: spage: %p, soffset: %u, dpage: %p, doffset: %u, nbytes: %u, priv: %p, ctx: %p.\n",
+			dev->name, spage, soff, dpage, doff, nbytes, priv, ctx);
+
+	hifn_setup_src_desc(dev, spage, soff, nbytes);
+	hifn_setup_cmd_desc(dev, ctx, priv, nbytes);
+	hifn_setup_dst_desc(dev, dpage, doff, nbytes);
+	hifn_setup_res_desc(dev);
+	return 0;
+}
+
 static int ablkcipher_walk_init(struct ablkcipher_walk *w,
 		int num, gfp_t gfp_flags)
 {
@@ -1431,7 +1435,7 @@
 		return -EINVAL;
 
 	while (size) {
-		copy = min(drest, src->length);
+		copy = min(drest, min(size, src->length));
 
 		saddr = kmap_atomic(sg_page(src), KM_SOFTIRQ1);
 		memcpy(daddr, saddr + src->offset, copy);
@@ -1458,10 +1462,6 @@
 static int ablkcipher_walk(struct ablkcipher_request *req,
 		struct ablkcipher_walk *w)
 {
-	unsigned blocksize =
-		crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(req));
-	unsigned alignmask =
-		crypto_ablkcipher_alignmask(crypto_ablkcipher_reqtfm(req));
 	struct scatterlist *src, *dst, *t;
 	void *daddr;
 	unsigned int nbytes = req->nbytes, offset, copy, diff;
@@ -1477,16 +1477,14 @@
 		dst = &req->dst[idx];
 
 		dprintk("\n%s: slen: %u, dlen: %u, soff: %u, doff: %u, offset: %u, "
-				"blocksize: %u, nbytes: %u.\n",
+				"nbytes: %u.\n",
 				__func__, src->length, dst->length, src->offset,
-				dst->offset, offset, blocksize, nbytes);
+				dst->offset, offset, nbytes);
 
-		if (src->length & (blocksize - 1) ||
-				src->offset & (alignmask - 1) ||
-				dst->length & (blocksize - 1) ||
-				dst->offset & (alignmask - 1) ||
-				offset) {
-			unsigned slen = src->length - offset;
+		if (!IS_ALIGNED(dst->offset, HIFN_D_DST_DALIGN) ||
+		    !IS_ALIGNED(dst->length, HIFN_D_DST_DALIGN) ||
+		    offset) {
+			unsigned slen = min(src->length - offset, nbytes);
 			unsigned dlen = PAGE_SIZE;
 
 			t = &w->cache[idx];
@@ -1498,8 +1496,8 @@
 
 			idx += err;
 
-			copy = slen & ~(blocksize - 1);
-			diff = slen & (blocksize - 1);
+			copy = slen & ~(HIFN_D_DST_DALIGN - 1);
+			diff = slen & (HIFN_D_DST_DALIGN - 1);
 
 			if (dlen < nbytes) {
 				/*
@@ -1507,7 +1505,7 @@
 				 * to put there additional blocksized chunk,
 				 * so we mark that page as containing only
 				 * blocksize aligned chunks:
-				 * 	t->length = (slen & ~(blocksize - 1));
+				 * 	t->length = (slen & ~(HIFN_D_DST_DALIGN - 1));
 				 * and increase number of bytes to be processed
 				 * in next chunk:
 				 * 	nbytes += diff;
@@ -1544,7 +1542,7 @@
 
 			kunmap_atomic(daddr, KM_SOFTIRQ0);
 		} else {
-			nbytes -= src->length;
+			nbytes -= min(src->length, nbytes);
 			idx++;
 		}
 
@@ -1563,14 +1561,10 @@
 	struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
 	struct hifn_device *dev = ctx->dev;
 	struct page *spage, *dpage;
-	unsigned long soff, doff, flags;
+	unsigned long soff, doff, dlen, flags;
 	unsigned int nbytes = req->nbytes, idx = 0, len;
 	int err = -EINVAL, sg_num;
 	struct scatterlist *src, *dst, *t;
-	unsigned blocksize =
-		crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(req));
-	unsigned alignmask =
-		crypto_ablkcipher_alignmask(crypto_ablkcipher_reqtfm(req));
 
 	if (ctx->iv && !ctx->ivsize && ctx->mode != ACRYPTO_MODE_ECB)
 		goto err_out_exit;
@@ -1578,17 +1572,14 @@
 	ctx->walk.flags = 0;
 
 	while (nbytes) {
-		src = &req->src[idx];
 		dst = &req->dst[idx];
+		dlen = min(dst->length, nbytes);
 
-		if (src->length & (blocksize - 1) ||
-				src->offset & (alignmask - 1) ||
-				dst->length & (blocksize - 1) ||
-				dst->offset & (alignmask - 1)) {
+		if (!IS_ALIGNED(dst->offset, HIFN_D_DST_DALIGN) ||
+		    !IS_ALIGNED(dlen, HIFN_D_DST_DALIGN))
 			ctx->walk.flags |= ASYNC_FLAGS_MISALIGNED;
-		}
 
-		nbytes -= src->length;
+		nbytes -= dlen;
 		idx++;
 	}
 
@@ -1602,7 +1593,10 @@
 	idx = 0;
 
 	sg_num = ablkcipher_walk(req, &ctx->walk);
-
+	if (sg_num < 0) {
+		err = sg_num;
+		goto err_out_exit;
+	}
 	atomic_set(&ctx->sg_num, sg_num);
 
 	spin_lock_irqsave(&dev->lock, flags);
@@ -1640,7 +1634,7 @@
 		if (err)
 			goto err_out;
 
-		nbytes -= len;
+		nbytes -= min(len, nbytes);
 	}
 
 	dev->active = HIFN_DEFAULT_ACTIVE_NUM;
@@ -1651,7 +1645,7 @@
 err_out:
 	spin_unlock_irqrestore(&dev->lock, flags);
 err_out_exit:
-	if (err && printk_ratelimit())
+	if (err)
 		dprintk("%s: iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
 				"type: %u, err: %d.\n",
 			dev->name, ctx->iv, ctx->ivsize,
@@ -1745,8 +1739,7 @@
 		return -EINVAL;
 
 	while (size) {
-
-		copy = min(dst->length, srest);
+		copy = min(srest, min(dst->length, size));
 
 		daddr = kmap_atomic(sg_page(dst), KM_IRQ0);
 		memcpy(daddr + dst->offset + offset, saddr, copy);
@@ -1803,7 +1796,7 @@
 					sg_page(dst), dst->length, nbytes);
 
 				if (!t->length) {
-					nbytes -= dst->length;
+					nbytes -= min(dst->length, nbytes);
 					idx++;
 					continue;
 				}
@@ -2202,9 +2195,9 @@
 		return err;
 
 	if (dev->started < HIFN_QUEUE_LENGTH &&	dev->queue.qlen)
-		err = hifn_process_queue(dev);
+		hifn_process_queue(dev);
 
-	return err;
+	return -EINPROGRESS;
 }
 
 /*
@@ -2364,7 +2357,7 @@
 	 * 3DES ECB, CBC, CFB and OFB modes.
 	 */
 	{
-		.name = "cfb(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+		.name = "cfb(des3_ede)", .drv_name = "cfb-3des", .bsize = 8,
 		.ablkcipher = {
 			.min_keysize	=	HIFN_3DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_3DES_KEY_LENGTH,
@@ -2374,7 +2367,7 @@
 		},
 	},
 	{
-		.name = "ofb(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+		.name = "ofb(des3_ede)", .drv_name = "ofb-3des", .bsize = 8,
 		.ablkcipher = {
 			.min_keysize	=	HIFN_3DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_3DES_KEY_LENGTH,
@@ -2384,8 +2377,9 @@
 		},
 	},
 	{
-		.name = "cbc(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+		.name = "cbc(des3_ede)", .drv_name = "cbc-3des", .bsize = 8,
 		.ablkcipher = {
+			.ivsize		=	HIFN_IV_LENGTH,
 			.min_keysize	=	HIFN_3DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_3DES_KEY_LENGTH,
 			.setkey		=	hifn_setkey,
@@ -2394,7 +2388,7 @@
 		},
 	},
 	{
-		.name = "ecb(des3_ede)", .drv_name = "hifn-3des", .bsize = 8,
+		.name = "ecb(des3_ede)", .drv_name = "ecb-3des", .bsize = 8,
 		.ablkcipher = {
 			.min_keysize	=	HIFN_3DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_3DES_KEY_LENGTH,
@@ -2408,7 +2402,7 @@
 	 * DES ECB, CBC, CFB and OFB modes.
 	 */
 	{
-		.name = "cfb(des)", .drv_name = "hifn-des", .bsize = 8,
+		.name = "cfb(des)", .drv_name = "cfb-des", .bsize = 8,
 		.ablkcipher = {
 			.min_keysize	=	HIFN_DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_DES_KEY_LENGTH,
@@ -2418,7 +2412,7 @@
 		},
 	},
 	{
-		.name = "ofb(des)", .drv_name = "hifn-des", .bsize = 8,
+		.name = "ofb(des)", .drv_name = "ofb-des", .bsize = 8,
 		.ablkcipher = {
 			.min_keysize	=	HIFN_DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_DES_KEY_LENGTH,
@@ -2428,8 +2422,9 @@
 		},
 	},
 	{
-		.name = "cbc(des)", .drv_name = "hifn-des", .bsize = 8,
+		.name = "cbc(des)", .drv_name = "cbc-des", .bsize = 8,
 		.ablkcipher = {
+			.ivsize		=	HIFN_IV_LENGTH,
 			.min_keysize	=	HIFN_DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_DES_KEY_LENGTH,
 			.setkey		=	hifn_setkey,
@@ -2438,7 +2433,7 @@
 		},
 	},
 	{
-		.name = "ecb(des)", .drv_name = "hifn-des", .bsize = 8,
+		.name = "ecb(des)", .drv_name = "ecb-des", .bsize = 8,
 		.ablkcipher = {
 			.min_keysize	=	HIFN_DES_KEY_LENGTH,
 			.max_keysize	=	HIFN_DES_KEY_LENGTH,
@@ -2452,7 +2447,7 @@
 	 * AES ECB, CBC, CFB and OFB modes.
 	 */
 	{
-		.name = "ecb(aes)", .drv_name = "hifn-aes", .bsize = 16,
+		.name = "ecb(aes)", .drv_name = "ecb-aes", .bsize = 16,
 		.ablkcipher = {
 			.min_keysize	=	AES_MIN_KEY_SIZE,
 			.max_keysize	=	AES_MAX_KEY_SIZE,
@@ -2462,8 +2457,9 @@
 		},
 	},
 	{
-		.name = "cbc(aes)", .drv_name = "hifn-aes", .bsize = 16,
+		.name = "cbc(aes)", .drv_name = "cbc-aes", .bsize = 16,
 		.ablkcipher = {
+			.ivsize		=	HIFN_AES_IV_LENGTH,
 			.min_keysize	=	AES_MIN_KEY_SIZE,
 			.max_keysize	=	AES_MAX_KEY_SIZE,
 			.setkey		=	hifn_setkey,
@@ -2472,7 +2468,7 @@
 		},
 	},
 	{
-		.name = "cfb(aes)", .drv_name = "hifn-aes", .bsize = 16,
+		.name = "cfb(aes)", .drv_name = "cfb-aes", .bsize = 16,
 		.ablkcipher = {
 			.min_keysize	=	AES_MIN_KEY_SIZE,
 			.max_keysize	=	AES_MAX_KEY_SIZE,
@@ -2482,7 +2478,7 @@
 		},
 	},
 	{
-		.name = "ofb(aes)", .drv_name = "hifn-aes", .bsize = 16,
+		.name = "ofb(aes)", .drv_name = "ofb-aes", .bsize = 16,
 		.ablkcipher = {
 			.min_keysize	=	AES_MIN_KEY_SIZE,
 			.max_keysize	=	AES_MAX_KEY_SIZE,
@@ -2514,15 +2510,14 @@
 		return -ENOMEM;
 
 	snprintf(alg->alg.cra_name, CRYPTO_MAX_ALG_NAME, "%s", t->name);
-	snprintf(alg->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s", t->drv_name);
+	snprintf(alg->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s-%s",
+		 t->drv_name, dev->name);
 
 	alg->alg.cra_priority = 300;
 	alg->alg.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC;
 	alg->alg.cra_blocksize = t->bsize;
 	alg->alg.cra_ctxsize = sizeof(struct hifn_context);
-	alg->alg.cra_alignmask = 15;
-	if (t->bsize == 8)
-		alg->alg.cra_alignmask = 3;
+	alg->alg.cra_alignmask = 0;
 	alg->alg.cra_type = &crypto_ablkcipher_type;
 	alg->alg.cra_module = THIS_MODULE;
 	alg->alg.cra_u.ablkcipher = t->ablkcipher;
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
new file mode 100644
index 0000000..42a107f
--- /dev/null
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -0,0 +1,1506 @@
+/*
+ * Intel IXP4xx NPE-C crypto driver
+ *
+ * Copyright (C) 2008 Christian Hohnstaedt <chohnstaedt@innominate.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+#include <linux/rtnetlink.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <crypto/ctr.h>
+#include <crypto/des.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <crypto/algapi.h>
+#include <crypto/aead.h>
+#include <crypto/authenc.h>
+#include <crypto/scatterwalk.h>
+
+#include <asm/arch/npe.h>
+#include <asm/arch/qmgr.h>
+
+#define MAX_KEYLEN 32
+
+/* hash: cfgword + 2 * digestlen; crypt: keylen + cfgword */
+#define NPE_CTX_LEN 80
+#define AES_BLOCK128 16
+
+#define NPE_OP_HASH_VERIFY   0x01
+#define NPE_OP_CCM_ENABLE    0x04
+#define NPE_OP_CRYPT_ENABLE  0x08
+#define NPE_OP_HASH_ENABLE   0x10
+#define NPE_OP_NOT_IN_PLACE  0x20
+#define NPE_OP_HMAC_DISABLE  0x40
+#define NPE_OP_CRYPT_ENCRYPT 0x80
+
+#define NPE_OP_CCM_GEN_MIC   0xcc
+#define NPE_OP_HASH_GEN_ICV  0x50
+#define NPE_OP_ENC_GEN_KEY   0xc9
+
+#define MOD_ECB     0x0000
+#define MOD_CTR     0x1000
+#define MOD_CBC_ENC 0x2000
+#define MOD_CBC_DEC 0x3000
+#define MOD_CCM_ENC 0x4000
+#define MOD_CCM_DEC 0x5000
+
+#define KEYLEN_128  4
+#define KEYLEN_192  6
+#define KEYLEN_256  8
+
+#define CIPH_DECR   0x0000
+#define CIPH_ENCR   0x0400
+
+#define MOD_DES     0x0000
+#define MOD_TDEA2   0x0100
+#define MOD_3DES   0x0200
+#define MOD_AES     0x0800
+#define MOD_AES128  (0x0800 | KEYLEN_128)
+#define MOD_AES192  (0x0900 | KEYLEN_192)
+#define MOD_AES256  (0x0a00 | KEYLEN_256)
+
+#define MAX_IVLEN   16
+#define NPE_ID      2  /* NPE C */
+#define NPE_QLEN    16
+/* Space for registering when the first
+ * NPE_QLEN crypt_ctl are busy */
+#define NPE_QLEN_TOTAL 64
+
+#define SEND_QID    29
+#define RECV_QID    30
+
+#define CTL_FLAG_UNUSED		0x0000
+#define CTL_FLAG_USED		0x1000
+#define CTL_FLAG_PERFORM_ABLK	0x0001
+#define CTL_FLAG_GEN_ICV	0x0002
+#define CTL_FLAG_GEN_REVAES	0x0004
+#define CTL_FLAG_PERFORM_AEAD	0x0008
+#define CTL_FLAG_MASK		0x000f
+
+#define HMAC_IPAD_VALUE   0x36
+#define HMAC_OPAD_VALUE   0x5C
+#define HMAC_PAD_BLOCKLEN SHA1_BLOCK_SIZE
+
+#define MD5_DIGEST_SIZE   16
+
+struct buffer_desc {
+	u32 phys_next;
+	u16 buf_len;
+	u16 pkt_len;
+	u32 phys_addr;
+	u32 __reserved[4];
+	struct buffer_desc *next;
+};
+
+struct crypt_ctl {
+	u8 mode;		/* NPE_OP_*  operation mode */
+	u8 init_len;
+	u16 reserved;
+	u8 iv[MAX_IVLEN];	/* IV for CBC mode or CTR IV for CTR mode */
+	u32 icv_rev_aes;	/* icv or rev aes */
+	u32 src_buf;
+	u32 dst_buf;
+	u16 auth_offs;		/* Authentication start offset */
+	u16 auth_len;		/* Authentication data length */
+	u16 crypt_offs;		/* Cryption start offset */
+	u16 crypt_len;		/* Cryption data length */
+	u32 aadAddr;		/* Additional Auth Data Addr for CCM mode */
+	u32 crypto_ctx;		/* NPE Crypto Param structure address */
+
+	/* Used by Host: 4*4 bytes*/
+	unsigned ctl_flags;
+	union {
+		struct ablkcipher_request *ablk_req;
+		struct aead_request *aead_req;
+		struct crypto_tfm *tfm;
+	} data;
+	struct buffer_desc *regist_buf;
+	u8 *regist_ptr;
+};
+
+struct ablk_ctx {
+	struct buffer_desc *src;
+	struct buffer_desc *dst;
+	unsigned src_nents;
+	unsigned dst_nents;
+};
+
+struct aead_ctx {
+	struct buffer_desc *buffer;
+	unsigned short assoc_nents;
+	unsigned short src_nents;
+	struct scatterlist ivlist;
+	/* used when the hmac is not on one sg entry */
+	u8 *hmac_virt;
+	int encrypt;
+};
+
+struct ix_hash_algo {
+	u32 cfgword;
+	unsigned char *icv;
+};
+
+struct ix_sa_dir {
+	unsigned char *npe_ctx;
+	dma_addr_t npe_ctx_phys;
+	int npe_ctx_idx;
+	u8 npe_mode;
+};
+
+struct ixp_ctx {
+	struct ix_sa_dir encrypt;
+	struct ix_sa_dir decrypt;
+	int authkey_len;
+	u8 authkey[MAX_KEYLEN];
+	int enckey_len;
+	u8 enckey[MAX_KEYLEN];
+	u8 salt[MAX_IVLEN];
+	u8 nonce[CTR_RFC3686_NONCE_SIZE];
+	unsigned salted;
+	atomic_t configuring;
+	struct completion completion;
+};
+
+struct ixp_alg {
+	struct crypto_alg crypto;
+	const struct ix_hash_algo *hash;
+	u32 cfg_enc;
+	u32 cfg_dec;
+
+	int registered;
+};
+
+static const struct ix_hash_algo hash_alg_md5 = {
+	.cfgword	= 0xAA010004,
+	.icv		= "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+			  "\xFE\xDC\xBA\x98\x76\x54\x32\x10",
+};
+static const struct ix_hash_algo hash_alg_sha1 = {
+	.cfgword	= 0x00000005,
+	.icv		= "\x67\x45\x23\x01\xEF\xCD\xAB\x89\x98\xBA"
+			  "\xDC\xFE\x10\x32\x54\x76\xC3\xD2\xE1\xF0",
+};
+
+static struct npe *npe_c;
+static struct dma_pool *buffer_pool = NULL;
+static struct dma_pool *ctx_pool = NULL;
+
+static struct crypt_ctl *crypt_virt = NULL;
+static dma_addr_t crypt_phys;
+
+static int support_aes = 1;
+
+static void dev_release(struct device *dev)
+{
+	return;
+}
+
+#define DRIVER_NAME "ixp4xx_crypto"
+static struct platform_device pseudo_dev = {
+	.name = DRIVER_NAME,
+	.id   = 0,
+	.num_resources = 0,
+	.dev  = {
+		.coherent_dma_mask = DMA_32BIT_MASK,
+		.release = dev_release,
+	}
+};
+
+static struct device *dev = &pseudo_dev.dev;
+
+static inline dma_addr_t crypt_virt2phys(struct crypt_ctl *virt)
+{
+	return crypt_phys + (virt - crypt_virt) * sizeof(struct crypt_ctl);
+}
+
+static inline struct crypt_ctl *crypt_phys2virt(dma_addr_t phys)
+{
+	return crypt_virt + (phys - crypt_phys) / sizeof(struct crypt_ctl);
+}
+
+static inline u32 cipher_cfg_enc(struct crypto_tfm *tfm)
+{
+	return container_of(tfm->__crt_alg, struct ixp_alg,crypto)->cfg_enc;
+}
+
+static inline u32 cipher_cfg_dec(struct crypto_tfm *tfm)
+{
+	return container_of(tfm->__crt_alg, struct ixp_alg,crypto)->cfg_dec;
+}
+
+static inline const struct ix_hash_algo *ix_hash(struct crypto_tfm *tfm)
+{
+	return container_of(tfm->__crt_alg, struct ixp_alg, crypto)->hash;
+}
+
+static int setup_crypt_desc(void)
+{
+	BUILD_BUG_ON(sizeof(struct crypt_ctl) != 64);
+	crypt_virt = dma_alloc_coherent(dev,
+			NPE_QLEN * sizeof(struct crypt_ctl),
+			&crypt_phys, GFP_KERNEL);
+	if (!crypt_virt)
+		return -ENOMEM;
+	memset(crypt_virt, 0, NPE_QLEN * sizeof(struct crypt_ctl));
+	return 0;
+}
+
+static spinlock_t desc_lock;
+static struct crypt_ctl *get_crypt_desc(void)
+{
+	int i;
+	static int idx = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&desc_lock, flags);
+
+	if (unlikely(!crypt_virt))
+		setup_crypt_desc();
+	if (unlikely(!crypt_virt)) {
+		spin_unlock_irqrestore(&desc_lock, flags);
+		return NULL;
+	}
+	i = idx;
+	if (crypt_virt[i].ctl_flags == CTL_FLAG_UNUSED) {
+		if (++idx >= NPE_QLEN)
+			idx = 0;
+		crypt_virt[i].ctl_flags = CTL_FLAG_USED;
+		spin_unlock_irqrestore(&desc_lock, flags);
+		return crypt_virt +i;
+	} else {
+		spin_unlock_irqrestore(&desc_lock, flags);
+		return NULL;
+	}
+}
+
+static spinlock_t emerg_lock;
+static struct crypt_ctl *get_crypt_desc_emerg(void)
+{
+	int i;
+	static int idx = NPE_QLEN;
+	struct crypt_ctl *desc;
+	unsigned long flags;
+
+	desc = get_crypt_desc();
+	if (desc)
+		return desc;
+	if (unlikely(!crypt_virt))
+		return NULL;
+
+	spin_lock_irqsave(&emerg_lock, flags);
+	i = idx;
+	if (crypt_virt[i].ctl_flags == CTL_FLAG_UNUSED) {
+		if (++idx >= NPE_QLEN_TOTAL)
+			idx = NPE_QLEN;
+		crypt_virt[i].ctl_flags = CTL_FLAG_USED;
+		spin_unlock_irqrestore(&emerg_lock, flags);
+		return crypt_virt +i;
+	} else {
+		spin_unlock_irqrestore(&emerg_lock, flags);
+		return NULL;
+	}
+}
+
+static void free_buf_chain(struct buffer_desc *buf, u32 phys)
+{
+	while (buf) {
+		struct buffer_desc *buf1;
+		u32 phys1;
+
+		buf1 = buf->next;
+		phys1 = buf->phys_next;
+		dma_pool_free(buffer_pool, buf, phys);
+		buf = buf1;
+		phys = phys1;
+	}
+}
+
+static struct tasklet_struct crypto_done_tasklet;
+
+static void finish_scattered_hmac(struct crypt_ctl *crypt)
+{
+	struct aead_request *req = crypt->data.aead_req;
+	struct aead_ctx *req_ctx = aead_request_ctx(req);
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	int authsize = crypto_aead_authsize(tfm);
+	int decryptlen = req->cryptlen - authsize;
+
+	if (req_ctx->encrypt) {
+		scatterwalk_map_and_copy(req_ctx->hmac_virt,
+			req->src, decryptlen, authsize, 1);
+	}
+	dma_pool_free(buffer_pool, req_ctx->hmac_virt, crypt->icv_rev_aes);
+}
+
+static void one_packet(dma_addr_t phys)
+{
+	struct crypt_ctl *crypt;
+	struct ixp_ctx *ctx;
+	int failed;
+	enum dma_data_direction src_direction = DMA_BIDIRECTIONAL;
+
+	failed = phys & 0x1 ? -EBADMSG : 0;
+	phys &= ~0x3;
+	crypt = crypt_phys2virt(phys);
+
+	switch (crypt->ctl_flags & CTL_FLAG_MASK) {
+	case CTL_FLAG_PERFORM_AEAD: {
+		struct aead_request *req = crypt->data.aead_req;
+		struct aead_ctx *req_ctx = aead_request_ctx(req);
+		dma_unmap_sg(dev, req->assoc, req_ctx->assoc_nents,
+				DMA_TO_DEVICE);
+		dma_unmap_sg(dev, &req_ctx->ivlist, 1, DMA_BIDIRECTIONAL);
+		dma_unmap_sg(dev, req->src, req_ctx->src_nents,
+				DMA_BIDIRECTIONAL);
+
+		free_buf_chain(req_ctx->buffer, crypt->src_buf);
+		if (req_ctx->hmac_virt) {
+			finish_scattered_hmac(crypt);
+		}
+		req->base.complete(&req->base, failed);
+		break;
+	}
+	case CTL_FLAG_PERFORM_ABLK: {
+		struct ablkcipher_request *req = crypt->data.ablk_req;
+		struct ablk_ctx *req_ctx = ablkcipher_request_ctx(req);
+		int nents;
+		if (req_ctx->dst) {
+			nents = req_ctx->dst_nents;
+			dma_unmap_sg(dev, req->dst, nents, DMA_FROM_DEVICE);
+			free_buf_chain(req_ctx->dst, crypt->dst_buf);
+			src_direction = DMA_TO_DEVICE;
+		}
+		nents = req_ctx->src_nents;
+		dma_unmap_sg(dev, req->src, nents, src_direction);
+		free_buf_chain(req_ctx->src, crypt->src_buf);
+		req->base.complete(&req->base, failed);
+		break;
+	}
+	case CTL_FLAG_GEN_ICV:
+		ctx = crypto_tfm_ctx(crypt->data.tfm);
+		dma_pool_free(ctx_pool, crypt->regist_ptr,
+				crypt->regist_buf->phys_addr);
+		dma_pool_free(buffer_pool, crypt->regist_buf, crypt->src_buf);
+		if (atomic_dec_and_test(&ctx->configuring))
+			complete(&ctx->completion);
+		break;
+	case CTL_FLAG_GEN_REVAES:
+		ctx = crypto_tfm_ctx(crypt->data.tfm);
+		*(u32*)ctx->decrypt.npe_ctx &= cpu_to_be32(~CIPH_ENCR);
+		if (atomic_dec_and_test(&ctx->configuring))
+			complete(&ctx->completion);
+		break;
+	default:
+		BUG();
+	}
+	crypt->ctl_flags = CTL_FLAG_UNUSED;
+}
+
+static void irqhandler(void *_unused)
+{
+	tasklet_schedule(&crypto_done_tasklet);
+}
+
+static void crypto_done_action(unsigned long arg)
+{
+	int i;
+
+	for(i=0; i<4; i++) {
+		dma_addr_t phys = qmgr_get_entry(RECV_QID);
+		if (!phys)
+			return;
+		one_packet(phys);
+	}
+	tasklet_schedule(&crypto_done_tasklet);
+}
+
+static int init_ixp_crypto(void)
+{
+	int ret = -ENODEV;
+
+	if (! ( ~(*IXP4XX_EXP_CFG2) & (IXP4XX_FEATURE_HASH |
+				IXP4XX_FEATURE_AES | IXP4XX_FEATURE_DES))) {
+		printk(KERN_ERR "ixp_crypto: No HW crypto available\n");
+		return ret;
+	}
+	npe_c = npe_request(NPE_ID);
+	if (!npe_c)
+		return ret;
+
+	if (!npe_running(npe_c)) {
+		npe_load_firmware(npe_c, npe_name(npe_c), dev);
+	}
+
+	/* buffer_pool will also be used to sometimes store the hmac,
+	 * so assure it is large enough
+	 */
+	BUILD_BUG_ON(SHA1_DIGEST_SIZE > sizeof(struct buffer_desc));
+	buffer_pool = dma_pool_create("buffer", dev,
+			sizeof(struct buffer_desc), 32, 0);
+	ret = -ENOMEM;
+	if (!buffer_pool) {
+		goto err;
+	}
+	ctx_pool = dma_pool_create("context", dev,
+			NPE_CTX_LEN, 16, 0);
+	if (!ctx_pool) {
+		goto err;
+	}
+	ret = qmgr_request_queue(SEND_QID, NPE_QLEN_TOTAL, 0, 0);
+	if (ret)
+		goto err;
+	ret = qmgr_request_queue(RECV_QID, NPE_QLEN, 0, 0);
+	if (ret) {
+		qmgr_release_queue(SEND_QID);
+		goto err;
+	}
+	qmgr_set_irq(RECV_QID, QUEUE_IRQ_SRC_NOT_EMPTY, irqhandler, NULL);
+	tasklet_init(&crypto_done_tasklet, crypto_done_action, 0);
+
+	qmgr_enable_irq(RECV_QID);
+	return 0;
+err:
+	if (ctx_pool)
+		dma_pool_destroy(ctx_pool);
+	if (buffer_pool)
+		dma_pool_destroy(buffer_pool);
+	npe_release(npe_c);
+	return ret;
+}
+
+static void release_ixp_crypto(void)
+{
+	qmgr_disable_irq(RECV_QID);
+	tasklet_kill(&crypto_done_tasklet);
+
+	qmgr_release_queue(SEND_QID);
+	qmgr_release_queue(RECV_QID);
+
+	dma_pool_destroy(ctx_pool);
+	dma_pool_destroy(buffer_pool);
+
+	npe_release(npe_c);
+
+	if (crypt_virt) {
+		dma_free_coherent(dev,
+			NPE_QLEN_TOTAL * sizeof( struct crypt_ctl),
+			crypt_virt, crypt_phys);
+	}
+	return;
+}
+
+static void reset_sa_dir(struct ix_sa_dir *dir)
+{
+	memset(dir->npe_ctx, 0, NPE_CTX_LEN);
+	dir->npe_ctx_idx = 0;
+	dir->npe_mode = 0;
+}
+
+static int init_sa_dir(struct ix_sa_dir *dir)
+{
+	dir->npe_ctx = dma_pool_alloc(ctx_pool, GFP_KERNEL, &dir->npe_ctx_phys);
+	if (!dir->npe_ctx) {
+		return -ENOMEM;
+	}
+	reset_sa_dir(dir);
+	return 0;
+}
+
+static void free_sa_dir(struct ix_sa_dir *dir)
+{
+	memset(dir->npe_ctx, 0, NPE_CTX_LEN);
+	dma_pool_free(ctx_pool, dir->npe_ctx, dir->npe_ctx_phys);
+}
+
+static int init_tfm(struct crypto_tfm *tfm)
+{
+	struct ixp_ctx *ctx = crypto_tfm_ctx(tfm);
+	int ret;
+
+	atomic_set(&ctx->configuring, 0);
+	ret = init_sa_dir(&ctx->encrypt);
+	if (ret)
+		return ret;
+	ret = init_sa_dir(&ctx->decrypt);
+	if (ret) {
+		free_sa_dir(&ctx->encrypt);
+	}
+	return ret;
+}
+
+static int init_tfm_ablk(struct crypto_tfm *tfm)
+{
+	tfm->crt_ablkcipher.reqsize = sizeof(struct ablk_ctx);
+	return init_tfm(tfm);
+}
+
+static int init_tfm_aead(struct crypto_tfm *tfm)
+{
+	tfm->crt_aead.reqsize = sizeof(struct aead_ctx);
+	return init_tfm(tfm);
+}
+
+static void exit_tfm(struct crypto_tfm *tfm)
+{
+	struct ixp_ctx *ctx = crypto_tfm_ctx(tfm);
+	free_sa_dir(&ctx->encrypt);
+	free_sa_dir(&ctx->decrypt);
+}
+
+static int register_chain_var(struct crypto_tfm *tfm, u8 xpad, u32 target,
+		int init_len, u32 ctx_addr, const u8 *key, int key_len)
+{
+	struct ixp_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypt_ctl *crypt;
+	struct buffer_desc *buf;
+	int i;
+	u8 *pad;
+	u32 pad_phys, buf_phys;
+
+	BUILD_BUG_ON(NPE_CTX_LEN < HMAC_PAD_BLOCKLEN);
+	pad = dma_pool_alloc(ctx_pool, GFP_KERNEL, &pad_phys);
+	if (!pad)
+		return -ENOMEM;
+	buf = dma_pool_alloc(buffer_pool, GFP_KERNEL, &buf_phys);
+	if (!buf) {
+		dma_pool_free(ctx_pool, pad, pad_phys);
+		return -ENOMEM;
+	}
+	crypt = get_crypt_desc_emerg();
+	if (!crypt) {
+		dma_pool_free(ctx_pool, pad, pad_phys);
+		dma_pool_free(buffer_pool, buf, buf_phys);
+		return -EAGAIN;
+	}
+
+	memcpy(pad, key, key_len);
+	memset(pad + key_len, 0, HMAC_PAD_BLOCKLEN - key_len);
+	for (i = 0; i < HMAC_PAD_BLOCKLEN; i++) {
+		pad[i] ^= xpad;
+	}
+
+	crypt->data.tfm = tfm;
+	crypt->regist_ptr = pad;
+	crypt->regist_buf = buf;
+
+	crypt->auth_offs = 0;
+	crypt->auth_len = HMAC_PAD_BLOCKLEN;
+	crypt->crypto_ctx = ctx_addr;
+	crypt->src_buf = buf_phys;
+	crypt->icv_rev_aes = target;
+	crypt->mode = NPE_OP_HASH_GEN_ICV;
+	crypt->init_len = init_len;
+	crypt->ctl_flags |= CTL_FLAG_GEN_ICV;
+
+	buf->next = 0;
+	buf->buf_len = HMAC_PAD_BLOCKLEN;
+	buf->pkt_len = 0;
+	buf->phys_addr = pad_phys;
+
+	atomic_inc(&ctx->configuring);
+	qmgr_put_entry(SEND_QID, crypt_virt2phys(crypt));
+	BUG_ON(qmgr_stat_overflow(SEND_QID));
+	return 0;
+}
+
+static int setup_auth(struct crypto_tfm *tfm, int encrypt, unsigned authsize,
+		const u8 *key, int key_len, unsigned digest_len)
+{
+	u32 itarget, otarget, npe_ctx_addr;
+	unsigned char *cinfo;
+	int init_len, ret = 0;
+	u32 cfgword;
+	struct ix_sa_dir *dir;
+	struct ixp_ctx *ctx = crypto_tfm_ctx(tfm);
+	const struct ix_hash_algo *algo;
+
+	dir = encrypt ? &ctx->encrypt : &ctx->decrypt;
+	cinfo = dir->npe_ctx + dir->npe_ctx_idx;
+	algo = ix_hash(tfm);
+
+	/* write cfg word to cryptinfo */
+	cfgword = algo->cfgword | ( authsize << 6); /* (authsize/4) << 8 */
+	*(u32*)cinfo = cpu_to_be32(cfgword);
+	cinfo += sizeof(cfgword);
+
+	/* write ICV to cryptinfo */
+	memcpy(cinfo, algo->icv, digest_len);
+	cinfo += digest_len;
+
+	itarget = dir->npe_ctx_phys + dir->npe_ctx_idx
+				+ sizeof(algo->cfgword);
+	otarget = itarget + digest_len;
+	init_len = cinfo - (dir->npe_ctx + dir->npe_ctx_idx);
+	npe_ctx_addr = dir->npe_ctx_phys + dir->npe_ctx_idx;
+
+	dir->npe_ctx_idx += init_len;
+	dir->npe_mode |= NPE_OP_HASH_ENABLE;
+
+	if (!encrypt)
+		dir->npe_mode |= NPE_OP_HASH_VERIFY;
+
+	ret = register_chain_var(tfm, HMAC_OPAD_VALUE, otarget,
+			init_len, npe_ctx_addr, key, key_len);
+	if (ret)
+		return ret;
+	return register_chain_var(tfm, HMAC_IPAD_VALUE, itarget,
+			init_len, npe_ctx_addr, key, key_len);
+}
+
+static int gen_rev_aes_key(struct crypto_tfm *tfm)
+{
+	struct crypt_ctl *crypt;
+	struct ixp_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct ix_sa_dir *dir = &ctx->decrypt;
+
+	crypt = get_crypt_desc_emerg();
+	if (!crypt) {
+		return -EAGAIN;
+	}
+	*(u32*)dir->npe_ctx |= cpu_to_be32(CIPH_ENCR);
+
+	crypt->data.tfm = tfm;
+	crypt->crypt_offs = 0;
+	crypt->crypt_len = AES_BLOCK128;
+	crypt->src_buf = 0;
+	crypt->crypto_ctx = dir->npe_ctx_phys;
+	crypt->icv_rev_aes = dir->npe_ctx_phys + sizeof(u32);
+	crypt->mode = NPE_OP_ENC_GEN_KEY;
+	crypt->init_len = dir->npe_ctx_idx;
+	crypt->ctl_flags |= CTL_FLAG_GEN_REVAES;
+
+	atomic_inc(&ctx->configuring);
+	qmgr_put_entry(SEND_QID, crypt_virt2phys(crypt));
+	BUG_ON(qmgr_stat_overflow(SEND_QID));
+	return 0;
+}
+
+static int setup_cipher(struct crypto_tfm *tfm, int encrypt,
+		const u8 *key, int key_len)
+{
+	u8 *cinfo;
+	u32 cipher_cfg;
+	u32 keylen_cfg = 0;
+	struct ix_sa_dir *dir;
+	struct ixp_ctx *ctx = crypto_tfm_ctx(tfm);
+	u32 *flags = &tfm->crt_flags;
+
+	dir = encrypt ? &ctx->encrypt : &ctx->decrypt;
+	cinfo = dir->npe_ctx;
+
+	if (encrypt) {
+		cipher_cfg = cipher_cfg_enc(tfm);
+		dir->npe_mode |= NPE_OP_CRYPT_ENCRYPT;
+	} else {
+		cipher_cfg = cipher_cfg_dec(tfm);
+	}
+	if (cipher_cfg & MOD_AES) {
+		switch (key_len) {
+			case 16: keylen_cfg = MOD_AES128 | KEYLEN_128; break;
+			case 24: keylen_cfg = MOD_AES192 | KEYLEN_192; break;
+			case 32: keylen_cfg = MOD_AES256 | KEYLEN_256; break;
+			default:
+				*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+				return -EINVAL;
+		}
+		cipher_cfg |= keylen_cfg;
+	} else if (cipher_cfg & MOD_3DES) {
+		const u32 *K = (const u32 *)key;
+		if (unlikely(!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
+			     !((K[2] ^ K[4]) | (K[3] ^ K[5]))))
+		{
+			*flags |= CRYPTO_TFM_RES_BAD_KEY_SCHED;
+			return -EINVAL;
+		}
+	} else {
+		u32 tmp[DES_EXPKEY_WORDS];
+		if (des_ekey(tmp, key) == 0) {
+			*flags |= CRYPTO_TFM_RES_WEAK_KEY;
+		}
+	}
+	/* write cfg word to cryptinfo */
+	*(u32*)cinfo = cpu_to_be32(cipher_cfg);
+	cinfo += sizeof(cipher_cfg);
+
+	/* write cipher key to cryptinfo */
+	memcpy(cinfo, key, key_len);
+	/* NPE wants keylen set to DES3_EDE_KEY_SIZE even for single DES */
+	if (key_len < DES3_EDE_KEY_SIZE && !(cipher_cfg & MOD_AES)) {
+		memset(cinfo + key_len, 0, DES3_EDE_KEY_SIZE -key_len);
+		key_len = DES3_EDE_KEY_SIZE;
+	}
+	dir->npe_ctx_idx = sizeof(cipher_cfg) + key_len;
+	dir->npe_mode |= NPE_OP_CRYPT_ENABLE;
+	if ((cipher_cfg & MOD_AES) && !encrypt) {
+		return gen_rev_aes_key(tfm);
+	}
+	return 0;
+}
+
+static int count_sg(struct scatterlist *sg, int nbytes)
+{
+	int i;
+	for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+		nbytes -= sg->length;
+	return i;
+}
+
+static struct buffer_desc *chainup_buffers(struct scatterlist *sg,
+			unsigned nbytes, struct buffer_desc *buf, gfp_t flags)
+{
+	int nents = 0;
+
+	while (nbytes > 0) {
+		struct buffer_desc *next_buf;
+		u32 next_buf_phys;
+		unsigned len = min(nbytes, sg_dma_len(sg));
+
+		nents++;
+		nbytes -= len;
+		if (!buf->phys_addr) {
+			buf->phys_addr = sg_dma_address(sg);
+			buf->buf_len = len;
+			buf->next = NULL;
+			buf->phys_next = 0;
+			goto next;
+		}
+		/* Two consecutive chunks on one page may be handled by the old
+		 * buffer descriptor, increased by the length of the new one
+		 */
+		if (sg_dma_address(sg) == buf->phys_addr + buf->buf_len) {
+			buf->buf_len += len;
+			goto next;
+		}
+		next_buf = dma_pool_alloc(buffer_pool, flags, &next_buf_phys);
+		if (!next_buf)
+			return NULL;
+		buf->next = next_buf;
+		buf->phys_next = next_buf_phys;
+
+		buf = next_buf;
+		buf->next = NULL;
+		buf->phys_next = 0;
+		buf->phys_addr = sg_dma_address(sg);
+		buf->buf_len = len;
+next:
+		if (nbytes > 0) {
+			sg = sg_next(sg);
+		}
+	}
+	return buf;
+}
+
+static int ablk_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
+			unsigned int key_len)
+{
+	struct ixp_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+	u32 *flags = &tfm->base.crt_flags;
+	int ret;
+
+	init_completion(&ctx->completion);
+	atomic_inc(&ctx->configuring);
+
+	reset_sa_dir(&ctx->encrypt);
+	reset_sa_dir(&ctx->decrypt);
+
+	ctx->encrypt.npe_mode = NPE_OP_HMAC_DISABLE;
+	ctx->decrypt.npe_mode = NPE_OP_HMAC_DISABLE;
+
+	ret = setup_cipher(&tfm->base, 0, key, key_len);
+	if (ret)
+		goto out;
+	ret = setup_cipher(&tfm->base, 1, key, key_len);
+	if (ret)
+		goto out;
+
+	if (*flags & CRYPTO_TFM_RES_WEAK_KEY) {
+		if (*flags & CRYPTO_TFM_REQ_WEAK_KEY) {
+			ret = -EINVAL;
+		} else {
+			*flags &= ~CRYPTO_TFM_RES_WEAK_KEY;
+		}
+	}
+out:
+	if (!atomic_dec_and_test(&ctx->configuring))
+		wait_for_completion(&ctx->completion);
+	return ret;
+}
+
+static int ablk_rfc3686_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
+		unsigned int key_len)
+{
+	struct ixp_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+
+	/* the nonce is stored in bytes at end of key */
+	if (key_len < CTR_RFC3686_NONCE_SIZE)
+		return -EINVAL;
+
+	memcpy(ctx->nonce, key + (key_len - CTR_RFC3686_NONCE_SIZE),
+			CTR_RFC3686_NONCE_SIZE);
+
+	key_len -= CTR_RFC3686_NONCE_SIZE;
+	return ablk_setkey(tfm, key, key_len);
+}
+
+static int ablk_perform(struct ablkcipher_request *req, int encrypt)
+{
+	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+	struct ixp_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+	unsigned ivsize = crypto_ablkcipher_ivsize(tfm);
+	int ret = -ENOMEM;
+	struct ix_sa_dir *dir;
+	struct crypt_ctl *crypt;
+	unsigned int nbytes = req->nbytes, nents;
+	enum dma_data_direction src_direction = DMA_BIDIRECTIONAL;
+	struct ablk_ctx *req_ctx = ablkcipher_request_ctx(req);
+	gfp_t flags = req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ?
+				GFP_KERNEL : GFP_ATOMIC;
+
+	if (qmgr_stat_full(SEND_QID))
+		return -EAGAIN;
+	if (atomic_read(&ctx->configuring))
+		return -EAGAIN;
+
+	dir = encrypt ? &ctx->encrypt : &ctx->decrypt;
+
+	crypt = get_crypt_desc();
+	if (!crypt)
+		return ret;
+
+	crypt->data.ablk_req = req;
+	crypt->crypto_ctx = dir->npe_ctx_phys;
+	crypt->mode = dir->npe_mode;
+	crypt->init_len = dir->npe_ctx_idx;
+
+	crypt->crypt_offs = 0;
+	crypt->crypt_len = nbytes;
+
+	BUG_ON(ivsize && !req->info);
+	memcpy(crypt->iv, req->info, ivsize);
+	if (req->src != req->dst) {
+		crypt->mode |= NPE_OP_NOT_IN_PLACE;
+		nents = count_sg(req->dst, nbytes);
+		/* This was never tested by Intel
+		 * for more than one dst buffer, I think. */
+		BUG_ON(nents != 1);
+		req_ctx->dst_nents = nents;
+		dma_map_sg(dev, req->dst, nents, DMA_FROM_DEVICE);
+		req_ctx->dst = dma_pool_alloc(buffer_pool, flags,&crypt->dst_buf);
+		if (!req_ctx->dst)
+			goto unmap_sg_dest;
+		req_ctx->dst->phys_addr = 0;
+		if (!chainup_buffers(req->dst, nbytes, req_ctx->dst, flags))
+			goto free_buf_dest;
+		src_direction = DMA_TO_DEVICE;
+	} else {
+		req_ctx->dst = NULL;
+		req_ctx->dst_nents = 0;
+	}
+	nents = count_sg(req->src, nbytes);
+	req_ctx->src_nents = nents;
+	dma_map_sg(dev, req->src, nents, src_direction);
+
+	req_ctx->src = dma_pool_alloc(buffer_pool, flags, &crypt->src_buf);
+	if (!req_ctx->src)
+		goto unmap_sg_src;
+	req_ctx->src->phys_addr = 0;
+	if (!chainup_buffers(req->src, nbytes, req_ctx->src, flags))
+		goto free_buf_src;
+
+	crypt->ctl_flags |= CTL_FLAG_PERFORM_ABLK;
+	qmgr_put_entry(SEND_QID, crypt_virt2phys(crypt));
+	BUG_ON(qmgr_stat_overflow(SEND_QID));
+	return -EINPROGRESS;
+
+free_buf_src:
+	free_buf_chain(req_ctx->src, crypt->src_buf);
+unmap_sg_src:
+	dma_unmap_sg(dev, req->src, req_ctx->src_nents, src_direction);
+free_buf_dest:
+	if (req->src != req->dst) {
+		free_buf_chain(req_ctx->dst, crypt->dst_buf);
+unmap_sg_dest:
+		dma_unmap_sg(dev, req->src, req_ctx->dst_nents,
+			DMA_FROM_DEVICE);
+	}
+	crypt->ctl_flags = CTL_FLAG_UNUSED;
+	return ret;
+}
+
+static int ablk_encrypt(struct ablkcipher_request *req)
+{
+	return ablk_perform(req, 1);
+}
+
+static int ablk_decrypt(struct ablkcipher_request *req)
+{
+	return ablk_perform(req, 0);
+}
+
+static int ablk_rfc3686_crypt(struct ablkcipher_request *req)
+{
+	struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+	struct ixp_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+	u8 iv[CTR_RFC3686_BLOCK_SIZE];
+	u8 *info = req->info;
+	int ret;
+
+	/* set up counter block */
+        memcpy(iv, ctx->nonce, CTR_RFC3686_NONCE_SIZE);
+	memcpy(iv + CTR_RFC3686_NONCE_SIZE, info, CTR_RFC3686_IV_SIZE);
+
+	/* initialize counter portion of counter block */
+	*(__be32 *)(iv + CTR_RFC3686_NONCE_SIZE + CTR_RFC3686_IV_SIZE) =
+		cpu_to_be32(1);
+
+	req->info = iv;
+	ret = ablk_perform(req, 1);
+	req->info = info;
+	return ret;
+}
+
+static int hmac_inconsistent(struct scatterlist *sg, unsigned start,
+		unsigned int nbytes)
+{
+	int offset = 0;
+
+	if (!nbytes)
+		return 0;
+
+	for (;;) {
+		if (start < offset + sg->length)
+			break;
+
+		offset += sg->length;
+		sg = sg_next(sg);
+	}
+	return (start + nbytes > offset + sg->length);
+}
+
+static int aead_perform(struct aead_request *req, int encrypt,
+		int cryptoffset, int eff_cryptlen, u8 *iv)
+{
+	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+	struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
+	unsigned ivsize = crypto_aead_ivsize(tfm);
+	unsigned authsize = crypto_aead_authsize(tfm);
+	int ret = -ENOMEM;
+	struct ix_sa_dir *dir;
+	struct crypt_ctl *crypt;
+	unsigned int cryptlen, nents;
+	struct buffer_desc *buf;
+	struct aead_ctx *req_ctx = aead_request_ctx(req);
+	gfp_t flags = req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ?
+				GFP_KERNEL : GFP_ATOMIC;
+
+	if (qmgr_stat_full(SEND_QID))
+		return -EAGAIN;
+	if (atomic_read(&ctx->configuring))
+		return -EAGAIN;
+
+	if (encrypt) {
+		dir = &ctx->encrypt;
+		cryptlen = req->cryptlen;
+	} else {
+		dir = &ctx->decrypt;
+		/* req->cryptlen includes the authsize when decrypting */
+		cryptlen = req->cryptlen -authsize;
+		eff_cryptlen -= authsize;
+	}
+	crypt = get_crypt_desc();
+	if (!crypt)
+		return ret;
+
+	crypt->data.aead_req = req;
+	crypt->crypto_ctx = dir->npe_ctx_phys;
+	crypt->mode = dir->npe_mode;
+	crypt->init_len = dir->npe_ctx_idx;
+
+	crypt->crypt_offs = cryptoffset;
+	crypt->crypt_len = eff_cryptlen;
+
+	crypt->auth_offs = 0;
+	crypt->auth_len = req->assoclen + ivsize + cryptlen;
+	BUG_ON(ivsize && !req->iv);
+	memcpy(crypt->iv, req->iv, ivsize);
+
+	if (req->src != req->dst) {
+		BUG(); /* -ENOTSUP because of my lazyness */
+	}
+
+	req_ctx->buffer = dma_pool_alloc(buffer_pool, flags, &crypt->src_buf);
+	if (!req_ctx->buffer)
+		goto out;
+	req_ctx->buffer->phys_addr = 0;
+	/* ASSOC data */
+	nents = count_sg(req->assoc, req->assoclen);
+	req_ctx->assoc_nents = nents;
+	dma_map_sg(dev, req->assoc, nents, DMA_TO_DEVICE);
+	buf = chainup_buffers(req->assoc, req->assoclen, req_ctx->buffer,flags);
+	if (!buf)
+		goto unmap_sg_assoc;
+	/* IV */
+	sg_init_table(&req_ctx->ivlist, 1);
+	sg_set_buf(&req_ctx->ivlist, iv, ivsize);
+	dma_map_sg(dev, &req_ctx->ivlist, 1, DMA_BIDIRECTIONAL);
+	buf = chainup_buffers(&req_ctx->ivlist, ivsize, buf, flags);
+	if (!buf)
+		goto unmap_sg_iv;
+	if (unlikely(hmac_inconsistent(req->src, cryptlen, authsize))) {
+		/* The 12 hmac bytes are scattered,
+		 * we need to copy them into a safe buffer */
+		req_ctx->hmac_virt = dma_pool_alloc(buffer_pool, flags,
+				&crypt->icv_rev_aes);
+		if (unlikely(!req_ctx->hmac_virt))
+			goto unmap_sg_iv;
+		if (!encrypt) {
+			scatterwalk_map_and_copy(req_ctx->hmac_virt,
+				req->src, cryptlen, authsize, 0);
+		}
+		req_ctx->encrypt = encrypt;
+	} else {
+		req_ctx->hmac_virt = NULL;
+	}
+	/* Crypt */
+	nents = count_sg(req->src, cryptlen + authsize);
+	req_ctx->src_nents = nents;
+	dma_map_sg(dev, req->src, nents, DMA_BIDIRECTIONAL);
+	buf = chainup_buffers(req->src, cryptlen + authsize, buf, flags);
+	if (!buf)
+		goto unmap_sg_src;
+	if (!req_ctx->hmac_virt) {
+		crypt->icv_rev_aes = buf->phys_addr + buf->buf_len - authsize;
+	}
+	crypt->ctl_flags |= CTL_FLAG_PERFORM_AEAD;
+	qmgr_put_entry(SEND_QID, crypt_virt2phys(crypt));
+	BUG_ON(qmgr_stat_overflow(SEND_QID));
+	return -EINPROGRESS;
+unmap_sg_src:
+	dma_unmap_sg(dev, req->src, req_ctx->src_nents, DMA_BIDIRECTIONAL);
+	if (req_ctx->hmac_virt) {
+		dma_pool_free(buffer_pool, req_ctx->hmac_virt,
+				crypt->icv_rev_aes);
+	}
+unmap_sg_iv:
+	dma_unmap_sg(dev, &req_ctx->ivlist, 1, DMA_BIDIRECTIONAL);
+unmap_sg_assoc:
+	dma_unmap_sg(dev, req->assoc, req_ctx->assoc_nents, DMA_TO_DEVICE);
+	free_buf_chain(req_ctx->buffer, crypt->src_buf);
+out:
+	crypt->ctl_flags = CTL_FLAG_UNUSED;
+	return ret;
+}
+
+static int aead_setup(struct crypto_aead *tfm, unsigned int authsize)
+{
+	struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
+	u32 *flags = &tfm->base.crt_flags;
+	unsigned digest_len = crypto_aead_alg(tfm)->maxauthsize;
+	int ret;
+
+	if (!ctx->enckey_len && !ctx->authkey_len)
+		return 0;
+	init_completion(&ctx->completion);
+	atomic_inc(&ctx->configuring);
+
+	reset_sa_dir(&ctx->encrypt);
+	reset_sa_dir(&ctx->decrypt);
+
+	ret = setup_cipher(&tfm->base, 0, ctx->enckey, ctx->enckey_len);
+	if (ret)
+		goto out;
+	ret = setup_cipher(&tfm->base, 1, ctx->enckey, ctx->enckey_len);
+	if (ret)
+		goto out;
+	ret = setup_auth(&tfm->base, 0, authsize, ctx->authkey,
+			ctx->authkey_len, digest_len);
+	if (ret)
+		goto out;
+	ret = setup_auth(&tfm->base, 1, authsize,  ctx->authkey,
+			ctx->authkey_len, digest_len);
+	if (ret)
+		goto out;
+
+	if (*flags & CRYPTO_TFM_RES_WEAK_KEY) {
+		if (*flags & CRYPTO_TFM_REQ_WEAK_KEY) {
+			ret = -EINVAL;
+			goto out;
+		} else {
+			*flags &= ~CRYPTO_TFM_RES_WEAK_KEY;
+		}
+	}
+out:
+	if (!atomic_dec_and_test(&ctx->configuring))
+		wait_for_completion(&ctx->completion);
+	return ret;
+}
+
+static int aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
+{
+	int max = crypto_aead_alg(tfm)->maxauthsize >> 2;
+
+	if ((authsize>>2) < 1 || (authsize>>2) > max || (authsize & 3))
+		return -EINVAL;
+	return aead_setup(tfm, authsize);
+}
+
+static int aead_setkey(struct crypto_aead *tfm, const u8 *key,
+			unsigned int keylen)
+{
+	struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
+	struct rtattr *rta = (struct rtattr *)key;
+	struct crypto_authenc_key_param *param;
+
+	if (!RTA_OK(rta, keylen))
+		goto badkey;
+	if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
+		goto badkey;
+	if (RTA_PAYLOAD(rta) < sizeof(*param))
+		goto badkey;
+
+	param = RTA_DATA(rta);
+	ctx->enckey_len = be32_to_cpu(param->enckeylen);
+
+	key += RTA_ALIGN(rta->rta_len);
+	keylen -= RTA_ALIGN(rta->rta_len);
+
+	if (keylen < ctx->enckey_len)
+		goto badkey;
+
+	ctx->authkey_len = keylen - ctx->enckey_len;
+	memcpy(ctx->enckey, key + ctx->authkey_len, ctx->enckey_len);
+	memcpy(ctx->authkey, key, ctx->authkey_len);
+
+	return aead_setup(tfm, crypto_aead_authsize(tfm));
+badkey:
+	ctx->enckey_len = 0;
+	crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+	return -EINVAL;
+}
+
+static int aead_encrypt(struct aead_request *req)
+{
+	unsigned ivsize = crypto_aead_ivsize(crypto_aead_reqtfm(req));
+	return aead_perform(req, 1, req->assoclen + ivsize,
+			req->cryptlen, req->iv);
+}
+
+static int aead_decrypt(struct aead_request *req)
+{
+	unsigned ivsize = crypto_aead_ivsize(crypto_aead_reqtfm(req));
+	return aead_perform(req, 0, req->assoclen + ivsize,
+			req->cryptlen, req->iv);
+}
+
+static int aead_givencrypt(struct aead_givcrypt_request *req)
+{
+	struct crypto_aead *tfm = aead_givcrypt_reqtfm(req);
+	struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
+	unsigned len, ivsize = crypto_aead_ivsize(tfm);
+	__be64 seq;
+
+	/* copied from eseqiv.c */
+	if (!ctx->salted) {
+		get_random_bytes(ctx->salt, ivsize);
+		ctx->salted = 1;
+	}
+	memcpy(req->areq.iv, ctx->salt, ivsize);
+	len = ivsize;
+	if (ivsize > sizeof(u64)) {
+		memset(req->giv, 0, ivsize - sizeof(u64));
+		len = sizeof(u64);
+	}
+	seq = cpu_to_be64(req->seq);
+	memcpy(req->giv + ivsize - len, &seq, len);
+	return aead_perform(&req->areq, 1, req->areq.assoclen,
+			req->areq.cryptlen +ivsize, req->giv);
+}
+
+static struct ixp_alg ixp4xx_algos[] = {
+{
+	.crypto	= {
+		.cra_name	= "cbc(des)",
+		.cra_blocksize	= DES_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= DES_KEY_SIZE,
+			.max_keysize	= DES_KEY_SIZE,
+			.ivsize		= DES_BLOCK_SIZE,
+			.geniv		= "eseqiv",
+			}
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_DES | MOD_CBC_ENC | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_DES | MOD_CBC_DEC | KEYLEN_192,
+
+}, {
+	.crypto	= {
+		.cra_name	= "ecb(des)",
+		.cra_blocksize	= DES_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= DES_KEY_SIZE,
+			.max_keysize	= DES_KEY_SIZE,
+			}
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_DES | MOD_ECB | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_DES | MOD_ECB | KEYLEN_192,
+}, {
+	.crypto	= {
+		.cra_name	= "cbc(des3_ede)",
+		.cra_blocksize	= DES3_EDE_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= DES3_EDE_KEY_SIZE,
+			.max_keysize	= DES3_EDE_KEY_SIZE,
+			.ivsize		= DES3_EDE_BLOCK_SIZE,
+			.geniv		= "eseqiv",
+			}
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_3DES | MOD_CBC_ENC | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_3DES | MOD_CBC_DEC | KEYLEN_192,
+}, {
+	.crypto	= {
+		.cra_name	= "ecb(des3_ede)",
+		.cra_blocksize	= DES3_EDE_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= DES3_EDE_KEY_SIZE,
+			.max_keysize	= DES3_EDE_KEY_SIZE,
+			}
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_3DES | MOD_ECB | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_3DES | MOD_ECB | KEYLEN_192,
+}, {
+	.crypto	= {
+		.cra_name	= "cbc(aes)",
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.geniv		= "eseqiv",
+			}
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_AES | MOD_CBC_ENC,
+	.cfg_dec = CIPH_DECR | MOD_AES | MOD_CBC_DEC,
+}, {
+	.crypto	= {
+		.cra_name	= "ecb(aes)",
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			}
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_AES | MOD_ECB,
+	.cfg_dec = CIPH_DECR | MOD_AES | MOD_ECB,
+}, {
+	.crypto	= {
+		.cra_name	= "ctr(aes)",
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.geniv		= "eseqiv",
+			}
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_AES | MOD_CTR,
+	.cfg_dec = CIPH_ENCR | MOD_AES | MOD_CTR,
+}, {
+	.crypto	= {
+		.cra_name	= "rfc3686(ctr(aes))",
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_u		= { .ablkcipher = {
+			.min_keysize	= AES_MIN_KEY_SIZE,
+			.max_keysize	= AES_MAX_KEY_SIZE,
+			.ivsize		= AES_BLOCK_SIZE,
+			.geniv		= "eseqiv",
+			.setkey		= ablk_rfc3686_setkey,
+			.encrypt	= ablk_rfc3686_crypt,
+			.decrypt	= ablk_rfc3686_crypt }
+		}
+	},
+	.cfg_enc = CIPH_ENCR | MOD_AES | MOD_CTR,
+	.cfg_dec = CIPH_ENCR | MOD_AES | MOD_CTR,
+}, {
+	.crypto	= {
+		.cra_name	= "authenc(hmac(md5),cbc(des))",
+		.cra_blocksize	= DES_BLOCK_SIZE,
+		.cra_u		= { .aead = {
+			.ivsize		= DES_BLOCK_SIZE,
+			.maxauthsize	= MD5_DIGEST_SIZE,
+			}
+		}
+	},
+	.hash = &hash_alg_md5,
+	.cfg_enc = CIPH_ENCR | MOD_DES | MOD_CBC_ENC | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_DES | MOD_CBC_DEC | KEYLEN_192,
+}, {
+	.crypto	= {
+		.cra_name	= "authenc(hmac(md5),cbc(des3_ede))",
+		.cra_blocksize	= DES3_EDE_BLOCK_SIZE,
+		.cra_u		= { .aead = {
+			.ivsize		= DES3_EDE_BLOCK_SIZE,
+			.maxauthsize	= MD5_DIGEST_SIZE,
+			}
+		}
+	},
+	.hash = &hash_alg_md5,
+	.cfg_enc = CIPH_ENCR | MOD_3DES | MOD_CBC_ENC | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_3DES | MOD_CBC_DEC | KEYLEN_192,
+}, {
+	.crypto	= {
+		.cra_name	= "authenc(hmac(sha1),cbc(des))",
+		.cra_blocksize	= DES_BLOCK_SIZE,
+		.cra_u		= { .aead = {
+			.ivsize		= DES_BLOCK_SIZE,
+			.maxauthsize	= SHA1_DIGEST_SIZE,
+			}
+		}
+	},
+	.hash = &hash_alg_sha1,
+	.cfg_enc = CIPH_ENCR | MOD_DES | MOD_CBC_ENC | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_DES | MOD_CBC_DEC | KEYLEN_192,
+}, {
+	.crypto	= {
+		.cra_name	= "authenc(hmac(sha1),cbc(des3_ede))",
+		.cra_blocksize	= DES3_EDE_BLOCK_SIZE,
+		.cra_u		= { .aead = {
+			.ivsize		= DES3_EDE_BLOCK_SIZE,
+			.maxauthsize	= SHA1_DIGEST_SIZE,
+			}
+		}
+	},
+	.hash = &hash_alg_sha1,
+	.cfg_enc = CIPH_ENCR | MOD_3DES | MOD_CBC_ENC | KEYLEN_192,
+	.cfg_dec = CIPH_DECR | MOD_3DES | MOD_CBC_DEC | KEYLEN_192,
+}, {
+	.crypto	= {
+		.cra_name	= "authenc(hmac(md5),cbc(aes))",
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_u		= { .aead = {
+			.ivsize		= AES_BLOCK_SIZE,
+			.maxauthsize	= MD5_DIGEST_SIZE,
+			}
+		}
+	},
+	.hash = &hash_alg_md5,
+	.cfg_enc = CIPH_ENCR | MOD_AES | MOD_CBC_ENC,
+	.cfg_dec = CIPH_DECR | MOD_AES | MOD_CBC_DEC,
+}, {
+	.crypto	= {
+		.cra_name	= "authenc(hmac(sha1),cbc(aes))",
+		.cra_blocksize	= AES_BLOCK_SIZE,
+		.cra_u		= { .aead = {
+			.ivsize		= AES_BLOCK_SIZE,
+			.maxauthsize	= SHA1_DIGEST_SIZE,
+			}
+		}
+	},
+	.hash = &hash_alg_sha1,
+	.cfg_enc = CIPH_ENCR | MOD_AES | MOD_CBC_ENC,
+	.cfg_dec = CIPH_DECR | MOD_AES | MOD_CBC_DEC,
+} };
+
+#define IXP_POSTFIX "-ixp4xx"
+static int __init ixp_module_init(void)
+{
+	int num = ARRAY_SIZE(ixp4xx_algos);
+	int i,err ;
+
+	if (platform_device_register(&pseudo_dev))
+		return -ENODEV;
+
+	spin_lock_init(&desc_lock);
+	spin_lock_init(&emerg_lock);
+
+	err = init_ixp_crypto();
+	if (err) {
+		platform_device_unregister(&pseudo_dev);
+		return err;
+	}
+	for (i=0; i< num; i++) {
+		struct crypto_alg *cra = &ixp4xx_algos[i].crypto;
+
+		if (snprintf(cra->cra_driver_name, CRYPTO_MAX_ALG_NAME,
+			"%s"IXP_POSTFIX, cra->cra_name) >=
+			CRYPTO_MAX_ALG_NAME)
+		{
+			continue;
+		}
+		if (!support_aes && (ixp4xx_algos[i].cfg_enc & MOD_AES)) {
+			continue;
+		}
+		if (!ixp4xx_algos[i].hash) {
+			/* block ciphers */
+			cra->cra_type = &crypto_ablkcipher_type;
+			cra->cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+					 CRYPTO_ALG_ASYNC;
+			if (!cra->cra_ablkcipher.setkey)
+				cra->cra_ablkcipher.setkey = ablk_setkey;
+			if (!cra->cra_ablkcipher.encrypt)
+				cra->cra_ablkcipher.encrypt = ablk_encrypt;
+			if (!cra->cra_ablkcipher.decrypt)
+				cra->cra_ablkcipher.decrypt = ablk_decrypt;
+			cra->cra_init = init_tfm_ablk;
+		} else {
+			/* authenc */
+			cra->cra_type = &crypto_aead_type;
+			cra->cra_flags = CRYPTO_ALG_TYPE_AEAD |
+					 CRYPTO_ALG_ASYNC;
+			cra->cra_aead.setkey = aead_setkey;
+			cra->cra_aead.setauthsize = aead_setauthsize;
+			cra->cra_aead.encrypt = aead_encrypt;
+			cra->cra_aead.decrypt = aead_decrypt;
+			cra->cra_aead.givencrypt = aead_givencrypt;
+			cra->cra_init = init_tfm_aead;
+		}
+		cra->cra_ctxsize = sizeof(struct ixp_ctx);
+		cra->cra_module = THIS_MODULE;
+		cra->cra_alignmask = 3;
+		cra->cra_priority = 300;
+		cra->cra_exit = exit_tfm;
+		if (crypto_register_alg(cra))
+			printk(KERN_ERR "Failed to register '%s'\n",
+				cra->cra_name);
+		else
+			ixp4xx_algos[i].registered = 1;
+	}
+	return 0;
+}
+
+static void __exit ixp_module_exit(void)
+{
+	int num = ARRAY_SIZE(ixp4xx_algos);
+	int i;
+
+	for (i=0; i< num; i++) {
+		if (ixp4xx_algos[i].registered)
+			crypto_unregister_alg(&ixp4xx_algos[i].crypto);
+	}
+	release_ixp_crypto();
+	platform_device_unregister(&pseudo_dev);
+}
+
+module_init(ixp_module_init);
+module_exit(ixp_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christian Hohnstaedt <chohnstaedt@innominate.com>");
+MODULE_DESCRIPTION("IXP4xx hardware crypto");
+
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index bb30eb9..54a2a16 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -385,12 +385,12 @@
 	int ret;
 
 	if (!cpu_has_xcrypt) {
-		printk(KERN_ERR PFX "VIA PadLock not detected.\n");
+		printk(KERN_NOTICE PFX "VIA PadLock not detected.\n");
 		return -ENODEV;
 	}
 
 	if (!cpu_has_xcrypt_enabled) {
-		printk(KERN_ERR PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
+		printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
 		return -ENODEV;
 	}
 
diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c
index c666b4e..40d5680 100644
--- a/drivers/crypto/padlock-sha.c
+++ b/drivers/crypto/padlock-sha.c
@@ -254,12 +254,12 @@
 	int rc = -ENODEV;
 
 	if (!cpu_has_phe) {
-		printk(KERN_ERR PFX "VIA PadLock Hash Engine not detected.\n");
+		printk(KERN_NOTICE PFX "VIA PadLock Hash Engine not detected.\n");
 		return -ENODEV;
 	}
 
 	if (!cpu_has_phe_enabled) {
-		printk(KERN_ERR PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
+		printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
 		return -ENODEV;
 	}
 
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
new file mode 100644
index 0000000..b11943d
--- /dev/null
+++ b/drivers/crypto/talitos.c
@@ -0,0 +1,1597 @@
+/*
+ * talitos - Freescale Integrated Security Engine (SEC) device driver
+ *
+ * Copyright (c) 2008 Freescale Semiconductor, Inc.
+ *
+ * Scatterlist Crypto API glue code copied from files with the following:
+ * Copyright (c) 2006-2007 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * Crypto algorithm registration code copied from hifn driver:
+ * 2007+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/crypto.h>
+#include <linux/hw_random.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/rtnetlink.h>
+
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/des.h>
+#include <crypto/sha.h>
+#include <crypto/aead.h>
+#include <crypto/authenc.h>
+
+#include "talitos.h"
+
+#define TALITOS_TIMEOUT 100000
+#define TALITOS_MAX_DATA_LEN 65535
+
+#define DESC_TYPE(desc_hdr) ((be32_to_cpu(desc_hdr) >> 3) & 0x1f)
+#define PRIMARY_EU(desc_hdr) ((be32_to_cpu(desc_hdr) >> 28) & 0xf)
+#define SECONDARY_EU(desc_hdr) ((be32_to_cpu(desc_hdr) >> 16) & 0xf)
+
+/* descriptor pointer entry */
+struct talitos_ptr {
+	__be16 len;	/* length */
+	u8 j_extent;	/* jump to sg link table and/or extent */
+	u8 eptr;	/* extended address */
+	__be32 ptr;	/* address */
+};
+
+/* descriptor */
+struct talitos_desc {
+	__be32 hdr;			/* header high bits */
+	__be32 hdr_lo;			/* header low bits */
+	struct talitos_ptr ptr[7];	/* ptr/len pair array */
+};
+
+/**
+ * talitos_request - descriptor submission request
+ * @desc: descriptor pointer (kernel virtual)
+ * @dma_desc: descriptor's physical bus address
+ * @callback: whom to call when descriptor processing is done
+ * @context: caller context (optional)
+ */
+struct talitos_request {
+	struct talitos_desc *desc;
+	dma_addr_t dma_desc;
+	void (*callback) (struct device *dev, struct talitos_desc *desc,
+	                  void *context, int error);
+	void *context;
+};
+
+struct talitos_private {
+	struct device *dev;
+	struct of_device *ofdev;
+	void __iomem *reg;
+	int irq;
+
+	/* SEC version geometry (from device tree node) */
+	unsigned int num_channels;
+	unsigned int chfifo_len;
+	unsigned int exec_units;
+	unsigned int desc_types;
+
+	/* next channel to be assigned next incoming descriptor */
+	atomic_t last_chan;
+
+	/* per-channel request fifo */
+	struct talitos_request **fifo;
+
+	/*
+	 * length of the request fifo
+	 * fifo_len is chfifo_len rounded up to next power of 2
+	 * so we can use bitwise ops to wrap
+	 */
+	unsigned int fifo_len;
+
+	/* per-channel index to next free descriptor request */
+	int *head;
+
+	/* per-channel index to next in-progress/done descriptor request */
+	int *tail;
+
+	/* per-channel request submission (head) and release (tail) locks */
+	spinlock_t *head_lock;
+	spinlock_t *tail_lock;
+
+	/* request callback tasklet */
+	struct tasklet_struct done_task;
+	struct tasklet_struct error_task;
+
+	/* list of registered algorithms */
+	struct list_head alg_list;
+
+	/* hwrng device */
+	struct hwrng rng;
+};
+
+/*
+ * map virtual single (contiguous) pointer to h/w descriptor pointer
+ */
+static void map_single_talitos_ptr(struct device *dev,
+				   struct talitos_ptr *talitos_ptr,
+				   unsigned short len, void *data,
+				   unsigned char extent,
+				   enum dma_data_direction dir)
+{
+	talitos_ptr->len = cpu_to_be16(len);
+	talitos_ptr->ptr = cpu_to_be32(dma_map_single(dev, data, len, dir));
+	talitos_ptr->j_extent = extent;
+}
+
+/*
+ * unmap bus single (contiguous) h/w descriptor pointer
+ */
+static void unmap_single_talitos_ptr(struct device *dev,
+				     struct talitos_ptr *talitos_ptr,
+				     enum dma_data_direction dir)
+{
+	dma_unmap_single(dev, be32_to_cpu(talitos_ptr->ptr),
+			 be16_to_cpu(talitos_ptr->len), dir);
+}
+
+static int reset_channel(struct device *dev, int ch)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	unsigned int timeout = TALITOS_TIMEOUT;
+
+	setbits32(priv->reg + TALITOS_CCCR(ch), TALITOS_CCCR_RESET);
+
+	while ((in_be32(priv->reg + TALITOS_CCCR(ch)) & TALITOS_CCCR_RESET)
+	       && --timeout)
+		cpu_relax();
+
+	if (timeout == 0) {
+		dev_err(dev, "failed to reset channel %d\n", ch);
+		return -EIO;
+	}
+
+	/* set done writeback and IRQ */
+	setbits32(priv->reg + TALITOS_CCCR_LO(ch), TALITOS_CCCR_LO_CDWE |
+		  TALITOS_CCCR_LO_CDIE);
+
+	return 0;
+}
+
+static int reset_device(struct device *dev)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	unsigned int timeout = TALITOS_TIMEOUT;
+
+	setbits32(priv->reg + TALITOS_MCR, TALITOS_MCR_SWR);
+
+	while ((in_be32(priv->reg + TALITOS_MCR) & TALITOS_MCR_SWR)
+	       && --timeout)
+		cpu_relax();
+
+	if (timeout == 0) {
+		dev_err(dev, "failed to reset device\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * Reset and initialize the device
+ */
+static int init_device(struct device *dev)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	int ch, err;
+
+	/*
+	 * Master reset
+	 * errata documentation: warning: certain SEC interrupts
+	 * are not fully cleared by writing the MCR:SWR bit,
+	 * set bit twice to completely reset
+	 */
+	err = reset_device(dev);
+	if (err)
+		return err;
+
+	err = reset_device(dev);
+	if (err)
+		return err;
+
+	/* reset channels */
+	for (ch = 0; ch < priv->num_channels; ch++) {
+		err = reset_channel(dev, ch);
+		if (err)
+			return err;
+	}
+
+	/* enable channel done and error interrupts */
+	setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
+	setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
+
+	return 0;
+}
+
+/**
+ * talitos_submit - submits a descriptor to the device for processing
+ * @dev:	the SEC device to be used
+ * @desc:	the descriptor to be processed by the device
+ * @callback:	whom to call when processing is complete
+ * @context:	a handle for use by caller (optional)
+ *
+ * desc must contain valid dma-mapped (bus physical) address pointers.
+ * callback must check err and feedback in descriptor header
+ * for device processing status.
+ */
+static int talitos_submit(struct device *dev, struct talitos_desc *desc,
+			  void (*callback)(struct device *dev,
+					   struct talitos_desc *desc,
+					   void *context, int error),
+			  void *context)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	struct talitos_request *request;
+	unsigned long flags, ch;
+	int head;
+
+	/* select done notification */
+	desc->hdr |= DESC_HDR_DONE_NOTIFY;
+
+	/* emulate SEC's round-robin channel fifo polling scheme */
+	ch = atomic_inc_return(&priv->last_chan) & (priv->num_channels - 1);
+
+	spin_lock_irqsave(&priv->head_lock[ch], flags);
+
+	head = priv->head[ch];
+	request = &priv->fifo[ch][head];
+
+	if (request->desc) {
+		/* request queue is full */
+		spin_unlock_irqrestore(&priv->head_lock[ch], flags);
+		return -EAGAIN;
+	}
+
+	/* map descriptor and save caller data */
+	request->dma_desc = dma_map_single(dev, desc, sizeof(*desc),
+					   DMA_BIDIRECTIONAL);
+	request->callback = callback;
+	request->context = context;
+
+	/* increment fifo head */
+	priv->head[ch] = (priv->head[ch] + 1) & (priv->fifo_len - 1);
+
+	smp_wmb();
+	request->desc = desc;
+
+	/* GO! */
+	wmb();
+	out_be32(priv->reg + TALITOS_FF_LO(ch), request->dma_desc);
+
+	spin_unlock_irqrestore(&priv->head_lock[ch], flags);
+
+	return -EINPROGRESS;
+}
+
+/*
+ * process what was done, notify callback of error if not
+ */
+static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	struct talitos_request *request, saved_req;
+	unsigned long flags;
+	int tail, status;
+
+	spin_lock_irqsave(&priv->tail_lock[ch], flags);
+
+	tail = priv->tail[ch];
+	while (priv->fifo[ch][tail].desc) {
+		request = &priv->fifo[ch][tail];
+
+		/* descriptors with their done bits set don't get the error */
+		rmb();
+		if ((request->desc->hdr & DESC_HDR_DONE) == DESC_HDR_DONE)
+			status = 0;
+		else
+			if (!error)
+				break;
+			else
+				status = error;
+
+		dma_unmap_single(dev, request->dma_desc,
+			sizeof(struct talitos_desc), DMA_BIDIRECTIONAL);
+
+		/* copy entries so we can call callback outside lock */
+		saved_req.desc = request->desc;
+		saved_req.callback = request->callback;
+		saved_req.context = request->context;
+
+		/* release request entry in fifo */
+		smp_wmb();
+		request->desc = NULL;
+
+		/* increment fifo tail */
+		priv->tail[ch] = (tail + 1) & (priv->fifo_len - 1);
+
+		spin_unlock_irqrestore(&priv->tail_lock[ch], flags);
+		saved_req.callback(dev, saved_req.desc, saved_req.context,
+				   status);
+		/* channel may resume processing in single desc error case */
+		if (error && !reset_ch && status == error)
+			return;
+		spin_lock_irqsave(&priv->tail_lock[ch], flags);
+		tail = priv->tail[ch];
+	}
+
+	spin_unlock_irqrestore(&priv->tail_lock[ch], flags);
+}
+
+/*
+ * process completed requests for channels that have done status
+ */
+static void talitos_done(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	int ch;
+
+	for (ch = 0; ch < priv->num_channels; ch++)
+		flush_channel(dev, ch, 0, 0);
+}
+
+/*
+ * locate current (offending) descriptor
+ */
+static struct talitos_desc *current_desc(struct device *dev, int ch)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	int tail = priv->tail[ch];
+	dma_addr_t cur_desc;
+
+	cur_desc = in_be32(priv->reg + TALITOS_CDPR_LO(ch));
+
+	while (priv->fifo[ch][tail].dma_desc != cur_desc) {
+		tail = (tail + 1) & (priv->fifo_len - 1);
+		if (tail == priv->tail[ch]) {
+			dev_err(dev, "couldn't locate current descriptor\n");
+			return NULL;
+		}
+	}
+
+	return priv->fifo[ch][tail].desc;
+}
+
+/*
+ * user diagnostics; report root cause of error based on execution unit status
+ */
+static void report_eu_error(struct device *dev, int ch, struct talitos_desc *desc)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	int i;
+
+	switch (desc->hdr & DESC_HDR_SEL0_MASK) {
+	case DESC_HDR_SEL0_AFEU:
+		dev_err(dev, "AFEUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_AFEUISR),
+			in_be32(priv->reg + TALITOS_AFEUISR_LO));
+		break;
+	case DESC_HDR_SEL0_DEU:
+		dev_err(dev, "DEUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_DEUISR),
+			in_be32(priv->reg + TALITOS_DEUISR_LO));
+		break;
+	case DESC_HDR_SEL0_MDEUA:
+	case DESC_HDR_SEL0_MDEUB:
+		dev_err(dev, "MDEUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_MDEUISR),
+			in_be32(priv->reg + TALITOS_MDEUISR_LO));
+		break;
+	case DESC_HDR_SEL0_RNG:
+		dev_err(dev, "RNGUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_RNGUISR),
+			in_be32(priv->reg + TALITOS_RNGUISR_LO));
+		break;
+	case DESC_HDR_SEL0_PKEU:
+		dev_err(dev, "PKEUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_PKEUISR),
+			in_be32(priv->reg + TALITOS_PKEUISR_LO));
+		break;
+	case DESC_HDR_SEL0_AESU:
+		dev_err(dev, "AESUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_AESUISR),
+			in_be32(priv->reg + TALITOS_AESUISR_LO));
+		break;
+	case DESC_HDR_SEL0_CRCU:
+		dev_err(dev, "CRCUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_CRCUISR),
+			in_be32(priv->reg + TALITOS_CRCUISR_LO));
+		break;
+	case DESC_HDR_SEL0_KEU:
+		dev_err(dev, "KEUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_KEUISR),
+			in_be32(priv->reg + TALITOS_KEUISR_LO));
+		break;
+	}
+
+	switch (desc->hdr & DESC_HDR_SEL1_MASK) {
+	case DESC_HDR_SEL1_MDEUA:
+	case DESC_HDR_SEL1_MDEUB:
+		dev_err(dev, "MDEUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_MDEUISR),
+			in_be32(priv->reg + TALITOS_MDEUISR_LO));
+		break;
+	case DESC_HDR_SEL1_CRCU:
+		dev_err(dev, "CRCUISR 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_CRCUISR),
+			in_be32(priv->reg + TALITOS_CRCUISR_LO));
+		break;
+	}
+
+	for (i = 0; i < 8; i++)
+		dev_err(dev, "DESCBUF 0x%08x_%08x\n",
+			in_be32(priv->reg + TALITOS_DESCBUF(ch) + 8*i),
+			in_be32(priv->reg + TALITOS_DESCBUF_LO(ch) + 8*i));
+}
+
+/*
+ * recover from error interrupts
+ */
+static void talitos_error(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	unsigned int timeout = TALITOS_TIMEOUT;
+	int ch, error, reset_dev = 0, reset_ch = 0;
+	u32 isr, isr_lo, v, v_lo;
+
+	isr = in_be32(priv->reg + TALITOS_ISR);
+	isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
+
+	for (ch = 0; ch < priv->num_channels; ch++) {
+		/* skip channels without errors */
+		if (!(isr & (1 << (ch * 2 + 1))))
+			continue;
+
+		error = -EINVAL;
+
+		v = in_be32(priv->reg + TALITOS_CCPSR(ch));
+		v_lo = in_be32(priv->reg + TALITOS_CCPSR_LO(ch));
+
+		if (v_lo & TALITOS_CCPSR_LO_DOF) {
+			dev_err(dev, "double fetch fifo overflow error\n");
+			error = -EAGAIN;
+			reset_ch = 1;
+		}
+		if (v_lo & TALITOS_CCPSR_LO_SOF) {
+			/* h/w dropped descriptor */
+			dev_err(dev, "single fetch fifo overflow error\n");
+			error = -EAGAIN;
+		}
+		if (v_lo & TALITOS_CCPSR_LO_MDTE)
+			dev_err(dev, "master data transfer error\n");
+		if (v_lo & TALITOS_CCPSR_LO_SGDLZ)
+			dev_err(dev, "s/g data length zero error\n");
+		if (v_lo & TALITOS_CCPSR_LO_FPZ)
+			dev_err(dev, "fetch pointer zero error\n");
+		if (v_lo & TALITOS_CCPSR_LO_IDH)
+			dev_err(dev, "illegal descriptor header error\n");
+		if (v_lo & TALITOS_CCPSR_LO_IEU)
+			dev_err(dev, "invalid execution unit error\n");
+		if (v_lo & TALITOS_CCPSR_LO_EU)
+			report_eu_error(dev, ch, current_desc(dev, ch));
+		if (v_lo & TALITOS_CCPSR_LO_GB)
+			dev_err(dev, "gather boundary error\n");
+		if (v_lo & TALITOS_CCPSR_LO_GRL)
+			dev_err(dev, "gather return/length error\n");
+		if (v_lo & TALITOS_CCPSR_LO_SB)
+			dev_err(dev, "scatter boundary error\n");
+		if (v_lo & TALITOS_CCPSR_LO_SRL)
+			dev_err(dev, "scatter return/length error\n");
+
+		flush_channel(dev, ch, error, reset_ch);
+
+		if (reset_ch) {
+			reset_channel(dev, ch);
+		} else {
+			setbits32(priv->reg + TALITOS_CCCR(ch),
+				  TALITOS_CCCR_CONT);
+			setbits32(priv->reg + TALITOS_CCCR_LO(ch), 0);
+			while ((in_be32(priv->reg + TALITOS_CCCR(ch)) &
+			       TALITOS_CCCR_CONT) && --timeout)
+				cpu_relax();
+			if (timeout == 0) {
+				dev_err(dev, "failed to restart channel %d\n",
+					ch);
+				reset_dev = 1;
+			}
+		}
+	}
+	if (reset_dev || isr & ~TALITOS_ISR_CHERR || isr_lo) {
+		dev_err(dev, "done overflow, internal time out, or rngu error: "
+		        "ISR 0x%08x_%08x\n", isr, isr_lo);
+
+		/* purge request queues */
+		for (ch = 0; ch < priv->num_channels; ch++)
+			flush_channel(dev, ch, -EIO, 1);
+
+		/* reset and reinitialize the device */
+		init_device(dev);
+	}
+}
+
+static irqreturn_t talitos_interrupt(int irq, void *data)
+{
+	struct device *dev = data;
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	u32 isr, isr_lo;
+
+	isr = in_be32(priv->reg + TALITOS_ISR);
+	isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
+
+	/* ack */
+	out_be32(priv->reg + TALITOS_ICR, isr);
+	out_be32(priv->reg + TALITOS_ICR_LO, isr_lo);
+
+	if (unlikely((isr & ~TALITOS_ISR_CHDONE) || isr_lo))
+		talitos_error((unsigned long)data);
+	else
+		if (likely(isr & TALITOS_ISR_CHDONE))
+			tasklet_schedule(&priv->done_task);
+
+	return (isr || isr_lo) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/*
+ * hwrng
+ */
+static int talitos_rng_data_present(struct hwrng *rng, int wait)
+{
+	struct device *dev = (struct device *)rng->priv;
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	u32 ofl;
+	int i;
+
+	for (i = 0; i < 20; i++) {
+		ofl = in_be32(priv->reg + TALITOS_RNGUSR_LO) &
+		      TALITOS_RNGUSR_LO_OFL;
+		if (ofl || !wait)
+			break;
+		udelay(10);
+	}
+
+	return !!ofl;
+}
+
+static int talitos_rng_data_read(struct hwrng *rng, u32 *data)
+{
+	struct device *dev = (struct device *)rng->priv;
+	struct talitos_private *priv = dev_get_drvdata(dev);
+
+	/* rng fifo requires 64-bit accesses */
+	*data = in_be32(priv->reg + TALITOS_RNGU_FIFO);
+	*data = in_be32(priv->reg + TALITOS_RNGU_FIFO_LO);
+
+	return sizeof(u32);
+}
+
+static int talitos_rng_init(struct hwrng *rng)
+{
+	struct device *dev = (struct device *)rng->priv;
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	unsigned int timeout = TALITOS_TIMEOUT;
+
+	setbits32(priv->reg + TALITOS_RNGURCR_LO, TALITOS_RNGURCR_LO_SR);
+	while (!(in_be32(priv->reg + TALITOS_RNGUSR_LO) & TALITOS_RNGUSR_LO_RD)
+	       && --timeout)
+		cpu_relax();
+	if (timeout == 0) {
+		dev_err(dev, "failed to reset rng hw\n");
+		return -ENODEV;
+	}
+
+	/* start generating */
+	setbits32(priv->reg + TALITOS_RNGUDSR_LO, 0);
+
+	return 0;
+}
+
+static int talitos_register_rng(struct device *dev)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+
+	priv->rng.name		= dev_driver_string(dev),
+	priv->rng.init		= talitos_rng_init,
+	priv->rng.data_present	= talitos_rng_data_present,
+	priv->rng.data_read	= talitos_rng_data_read,
+	priv->rng.priv		= (unsigned long)dev;
+
+	return hwrng_register(&priv->rng);
+}
+
+static void talitos_unregister_rng(struct device *dev)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+
+	hwrng_unregister(&priv->rng);
+}
+
+/*
+ * crypto alg
+ */
+#define TALITOS_CRA_PRIORITY		3000
+#define TALITOS_MAX_KEY_SIZE		64
+#define TALITOS_MAX_IV_LENGTH		16 /* max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */
+
+#define MD5_DIGEST_SIZE   16
+
+struct talitos_ctx {
+	struct device *dev;
+	__be32 desc_hdr_template;
+	u8 key[TALITOS_MAX_KEY_SIZE];
+	u8 iv[TALITOS_MAX_IV_LENGTH];
+	unsigned int keylen;
+	unsigned int enckeylen;
+	unsigned int authkeylen;
+	unsigned int authsize;
+};
+
+static int aead_authenc_setauthsize(struct crypto_aead *authenc,
+						 unsigned int authsize)
+{
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+
+	ctx->authsize = authsize;
+
+	return 0;
+}
+
+static int aead_authenc_setkey(struct crypto_aead *authenc,
+					    const u8 *key, unsigned int keylen)
+{
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+	struct rtattr *rta = (void *)key;
+	struct crypto_authenc_key_param *param;
+	unsigned int authkeylen;
+	unsigned int enckeylen;
+
+	if (!RTA_OK(rta, keylen))
+		goto badkey;
+
+	if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
+		goto badkey;
+
+	if (RTA_PAYLOAD(rta) < sizeof(*param))
+		goto badkey;
+
+	param = RTA_DATA(rta);
+	enckeylen = be32_to_cpu(param->enckeylen);
+
+	key += RTA_ALIGN(rta->rta_len);
+	keylen -= RTA_ALIGN(rta->rta_len);
+
+	if (keylen < enckeylen)
+		goto badkey;
+
+	authkeylen = keylen - enckeylen;
+
+	if (keylen > TALITOS_MAX_KEY_SIZE)
+		goto badkey;
+
+	memcpy(&ctx->key, key, keylen);
+
+	ctx->keylen = keylen;
+	ctx->enckeylen = enckeylen;
+	ctx->authkeylen = authkeylen;
+
+	return 0;
+
+badkey:
+	crypto_aead_set_flags(authenc, CRYPTO_TFM_RES_BAD_KEY_LEN);
+	return -EINVAL;
+}
+
+/*
+ * ipsec_esp_edesc - s/w-extended ipsec_esp descriptor
+ * @src_nents: number of segments in input scatterlist
+ * @dst_nents: number of segments in output scatterlist
+ * @dma_len: length of dma mapped link_tbl space
+ * @dma_link_tbl: bus physical address of link_tbl
+ * @desc: h/w descriptor
+ * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1)
+ *
+ * if decrypting (with authcheck), or either one of src_nents or dst_nents
+ * is greater than 1, an integrity check value is concatenated to the end
+ * of link_tbl data
+ */
+struct ipsec_esp_edesc {
+	int src_nents;
+	int dst_nents;
+	int dma_len;
+	dma_addr_t dma_link_tbl;
+	struct talitos_desc desc;
+	struct talitos_ptr link_tbl[0];
+};
+
+static void ipsec_esp_unmap(struct device *dev,
+			    struct ipsec_esp_edesc *edesc,
+			    struct aead_request *areq)
+{
+	unmap_single_talitos_ptr(dev, &edesc->desc.ptr[6], DMA_FROM_DEVICE);
+	unmap_single_talitos_ptr(dev, &edesc->desc.ptr[3], DMA_TO_DEVICE);
+	unmap_single_talitos_ptr(dev, &edesc->desc.ptr[2], DMA_TO_DEVICE);
+	unmap_single_talitos_ptr(dev, &edesc->desc.ptr[0], DMA_TO_DEVICE);
+
+	dma_unmap_sg(dev, areq->assoc, 1, DMA_TO_DEVICE);
+
+	if (areq->src != areq->dst) {
+		dma_unmap_sg(dev, areq->src, edesc->src_nents ? : 1,
+			     DMA_TO_DEVICE);
+		dma_unmap_sg(dev, areq->dst, edesc->dst_nents ? : 1,
+			     DMA_FROM_DEVICE);
+	} else {
+		dma_unmap_sg(dev, areq->src, edesc->src_nents ? : 1,
+			     DMA_BIDIRECTIONAL);
+	}
+
+	if (edesc->dma_len)
+		dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
+				 DMA_BIDIRECTIONAL);
+}
+
+/*
+ * ipsec_esp descriptor callbacks
+ */
+static void ipsec_esp_encrypt_done(struct device *dev,
+				   struct talitos_desc *desc, void *context,
+				   int err)
+{
+	struct aead_request *areq = context;
+	struct ipsec_esp_edesc *edesc =
+		 container_of(desc, struct ipsec_esp_edesc, desc);
+	struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+	struct scatterlist *sg;
+	void *icvdata;
+
+	ipsec_esp_unmap(dev, edesc, areq);
+
+	/* copy the generated ICV to dst */
+	if (edesc->dma_len) {
+		icvdata = &edesc->link_tbl[edesc->src_nents +
+					   edesc->dst_nents + 1];
+		sg = sg_last(areq->dst, edesc->dst_nents);
+		memcpy((char *)sg_virt(sg) + sg->length - ctx->authsize,
+		       icvdata, ctx->authsize);
+	}
+
+	kfree(edesc);
+
+	aead_request_complete(areq, err);
+}
+
+static void ipsec_esp_decrypt_done(struct device *dev,
+				   struct talitos_desc *desc, void *context,
+				   int err)
+{
+	struct aead_request *req = context;
+	struct ipsec_esp_edesc *edesc =
+		 container_of(desc, struct ipsec_esp_edesc, desc);
+	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+	struct scatterlist *sg;
+	void *icvdata;
+
+	ipsec_esp_unmap(dev, edesc, req);
+
+	if (!err) {
+		/* auth check */
+		if (edesc->dma_len)
+			icvdata = &edesc->link_tbl[edesc->src_nents +
+						   edesc->dst_nents + 1];
+		else
+			icvdata = &edesc->link_tbl[0];
+
+		sg = sg_last(req->dst, edesc->dst_nents ? : 1);
+		err = memcmp(icvdata, (char *)sg_virt(sg) + sg->length -
+			     ctx->authsize, ctx->authsize) ? -EBADMSG : 0;
+	}
+
+	kfree(edesc);
+
+	aead_request_complete(req, err);
+}
+
+/*
+ * convert scatterlist to SEC h/w link table format
+ * stop at cryptlen bytes
+ */
+static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
+			   int cryptlen, struct talitos_ptr *link_tbl_ptr)
+{
+	int n_sg = sg_count;
+
+	while (n_sg--) {
+		link_tbl_ptr->ptr = cpu_to_be32(sg_dma_address(sg));
+		link_tbl_ptr->len = cpu_to_be16(sg_dma_len(sg));
+		link_tbl_ptr->j_extent = 0;
+		link_tbl_ptr++;
+		cryptlen -= sg_dma_len(sg);
+		sg = sg_next(sg);
+	}
+
+	/* adjust (decrease) last one (or two) entry's len to cryptlen */
+	link_tbl_ptr--;
+	while (link_tbl_ptr->len <= (-cryptlen)) {
+		/* Empty this entry, and move to previous one */
+		cryptlen += be16_to_cpu(link_tbl_ptr->len);
+		link_tbl_ptr->len = 0;
+		sg_count--;
+		link_tbl_ptr--;
+	}
+	link_tbl_ptr->len = cpu_to_be16(be16_to_cpu(link_tbl_ptr->len)
+					+ cryptlen);
+
+	/* tag end of link table */
+	link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
+
+	return sg_count;
+}
+
+/*
+ * fill in and submit ipsec_esp descriptor
+ */
+static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
+		     u8 *giv, u64 seq,
+		     void (*callback) (struct device *dev,
+				       struct talitos_desc *desc,
+				       void *context, int error))
+{
+	struct crypto_aead *aead = crypto_aead_reqtfm(areq);
+	struct talitos_ctx *ctx = crypto_aead_ctx(aead);
+	struct device *dev = ctx->dev;
+	struct talitos_desc *desc = &edesc->desc;
+	unsigned int cryptlen = areq->cryptlen;
+	unsigned int authsize = ctx->authsize;
+	unsigned int ivsize;
+	int sg_count;
+
+	/* hmac key */
+	map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
+			       0, DMA_TO_DEVICE);
+	/* hmac data */
+	map_single_talitos_ptr(dev, &desc->ptr[1], sg_virt(areq->src) -
+			       sg_virt(areq->assoc), sg_virt(areq->assoc), 0,
+			       DMA_TO_DEVICE);
+	/* cipher iv */
+	ivsize = crypto_aead_ivsize(aead);
+	map_single_talitos_ptr(dev, &desc->ptr[2], ivsize, giv ?: areq->iv, 0,
+			       DMA_TO_DEVICE);
+
+	/* cipher key */
+	map_single_talitos_ptr(dev, &desc->ptr[3], ctx->enckeylen,
+			       (char *)&ctx->key + ctx->authkeylen, 0,
+			       DMA_TO_DEVICE);
+
+	/*
+	 * cipher in
+	 * map and adjust cipher len to aead request cryptlen.
+	 * extent is bytes of HMAC postpended to ciphertext,
+	 * typically 12 for ipsec
+	 */
+	desc->ptr[4].len = cpu_to_be16(cryptlen);
+	desc->ptr[4].j_extent = authsize;
+
+	if (areq->src == areq->dst)
+		sg_count = dma_map_sg(dev, areq->src, edesc->src_nents ? : 1,
+				      DMA_BIDIRECTIONAL);
+	else
+		sg_count = dma_map_sg(dev, areq->src, edesc->src_nents ? : 1,
+				      DMA_TO_DEVICE);
+
+	if (sg_count == 1) {
+		desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
+	} else {
+		sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen,
+					  &edesc->link_tbl[0]);
+		if (sg_count > 1) {
+			desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
+			desc->ptr[4].ptr = cpu_to_be32(edesc->dma_link_tbl);
+			dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
+						   edesc->dma_len, DMA_BIDIRECTIONAL);
+		} else {
+			/* Only one segment now, so no link tbl needed */
+			desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
+		}
+	}
+
+	/* cipher out */
+	desc->ptr[5].len = cpu_to_be16(cryptlen);
+	desc->ptr[5].j_extent = authsize;
+
+	if (areq->src != areq->dst) {
+		sg_count = dma_map_sg(dev, areq->dst, edesc->dst_nents ? : 1,
+				      DMA_FROM_DEVICE);
+	}
+
+	if (sg_count == 1) {
+		desc->ptr[5].ptr = cpu_to_be32(sg_dma_address(areq->dst));
+	} else {
+		struct talitos_ptr *link_tbl_ptr =
+			&edesc->link_tbl[edesc->src_nents];
+		struct scatterlist *sg;
+
+		desc->ptr[5].ptr = cpu_to_be32((struct talitos_ptr *)
+					       edesc->dma_link_tbl +
+					       edesc->src_nents);
+		if (areq->src == areq->dst) {
+			memcpy(link_tbl_ptr, &edesc->link_tbl[0],
+			       edesc->src_nents * sizeof(struct talitos_ptr));
+		} else {
+			sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
+						  link_tbl_ptr);
+		}
+		link_tbl_ptr += sg_count - 1;
+
+		/* handle case where sg_last contains the ICV exclusively */
+		sg = sg_last(areq->dst, edesc->dst_nents);
+		if (sg->length == ctx->authsize)
+			link_tbl_ptr--;
+
+		link_tbl_ptr->j_extent = 0;
+		link_tbl_ptr++;
+		link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
+		link_tbl_ptr->len = cpu_to_be16(authsize);
+
+		/* icv data follows link tables */
+		link_tbl_ptr->ptr = cpu_to_be32((struct talitos_ptr *)
+						edesc->dma_link_tbl +
+					        edesc->src_nents +
+						edesc->dst_nents + 1);
+
+		desc->ptr[5].j_extent |= DESC_PTR_LNKTBL_JUMP;
+		dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
+					   edesc->dma_len, DMA_BIDIRECTIONAL);
+	}
+
+	/* iv out */
+	map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, 0,
+			       DMA_FROM_DEVICE);
+
+	return talitos_submit(dev, desc, callback, areq);
+}
+
+
+/*
+ * derive number of elements in scatterlist
+ */
+static int sg_count(struct scatterlist *sg_list, int nbytes)
+{
+	struct scatterlist *sg = sg_list;
+	int sg_nents = 0;
+
+	while (nbytes) {
+		sg_nents++;
+		nbytes -= sg->length;
+		sg = sg_next(sg);
+	}
+
+	return sg_nents;
+}
+
+/*
+ * allocate and map the ipsec_esp extended descriptor
+ */
+static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
+						     int icv_stashing)
+{
+	struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+	struct ipsec_esp_edesc *edesc;
+	int src_nents, dst_nents, alloc_len, dma_len;
+
+	if (areq->cryptlen + ctx->authsize > TALITOS_MAX_DATA_LEN) {
+		dev_err(ctx->dev, "cryptlen exceeds h/w max limit\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	src_nents = sg_count(areq->src, areq->cryptlen + ctx->authsize);
+	src_nents = (src_nents == 1) ? 0 : src_nents;
+
+	if (areq->dst == areq->src) {
+		dst_nents = src_nents;
+	} else {
+		dst_nents = sg_count(areq->dst, areq->cryptlen + ctx->authsize);
+		dst_nents = (dst_nents == 1) ? 0 : src_nents;
+	}
+
+	/*
+	 * allocate space for base edesc plus the link tables,
+	 * allowing for a separate entry for the generated ICV (+ 1),
+	 * and the ICV data itself
+	 */
+	alloc_len = sizeof(struct ipsec_esp_edesc);
+	if (src_nents || dst_nents) {
+		dma_len = (src_nents + dst_nents + 1) *
+				 sizeof(struct talitos_ptr) + ctx->authsize;
+		alloc_len += dma_len;
+	} else {
+		dma_len = 0;
+		alloc_len += icv_stashing ? ctx->authsize : 0;
+	}
+
+	edesc = kmalloc(alloc_len, GFP_DMA);
+	if (!edesc) {
+		dev_err(ctx->dev, "could not allocate edescriptor\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	edesc->src_nents = src_nents;
+	edesc->dst_nents = dst_nents;
+	edesc->dma_len = dma_len;
+	edesc->dma_link_tbl = dma_map_single(ctx->dev, &edesc->link_tbl[0],
+					     edesc->dma_len, DMA_BIDIRECTIONAL);
+
+	return edesc;
+}
+
+static int aead_authenc_encrypt(struct aead_request *req)
+{
+	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+	struct ipsec_esp_edesc *edesc;
+
+	/* allocate extended descriptor */
+	edesc = ipsec_esp_edesc_alloc(req, 0);
+	if (IS_ERR(edesc))
+		return PTR_ERR(edesc);
+
+	/* set encrypt */
+	edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_MODE0_ENCRYPT;
+
+	return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_encrypt_done);
+}
+
+static int aead_authenc_decrypt(struct aead_request *req)
+{
+	struct crypto_aead *authenc = crypto_aead_reqtfm(req);
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+	unsigned int authsize = ctx->authsize;
+	struct ipsec_esp_edesc *edesc;
+	struct scatterlist *sg;
+	void *icvdata;
+
+	req->cryptlen -= authsize;
+
+	/* allocate extended descriptor */
+	edesc = ipsec_esp_edesc_alloc(req, 1);
+	if (IS_ERR(edesc))
+		return PTR_ERR(edesc);
+
+	/* stash incoming ICV for later cmp with ICV generated by the h/w */
+	if (edesc->dma_len)
+		icvdata = &edesc->link_tbl[edesc->src_nents +
+					   edesc->dst_nents + 1];
+	else
+		icvdata = &edesc->link_tbl[0];
+
+	sg = sg_last(req->src, edesc->src_nents ? : 1);
+
+	memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
+	       ctx->authsize);
+
+	/* decrypt */
+	edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+
+	return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_done);
+}
+
+static int aead_authenc_givencrypt(
+	struct aead_givcrypt_request *req)
+{
+	struct aead_request *areq = &req->areq;
+	struct crypto_aead *authenc = crypto_aead_reqtfm(areq);
+	struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
+	struct ipsec_esp_edesc *edesc;
+
+	/* allocate extended descriptor */
+	edesc = ipsec_esp_edesc_alloc(areq, 0);
+	if (IS_ERR(edesc))
+		return PTR_ERR(edesc);
+
+	/* set encrypt */
+	edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_MODE0_ENCRYPT;
+
+	memcpy(req->giv, ctx->iv, crypto_aead_ivsize(authenc));
+
+	return ipsec_esp(edesc, areq, req->giv, req->seq,
+			 ipsec_esp_encrypt_done);
+}
+
+struct talitos_alg_template {
+	char name[CRYPTO_MAX_ALG_NAME];
+	char driver_name[CRYPTO_MAX_ALG_NAME];
+	unsigned int blocksize;
+	struct aead_alg aead;
+	struct device *dev;
+	__be32 desc_hdr_template;
+};
+
+static struct talitos_alg_template driver_algs[] = {
+	/* single-pass ipsec_esp descriptor */
+	{
+		.name = "authenc(hmac(sha1),cbc(aes))",
+		.driver_name = "authenc-hmac-sha1-cbc-aes-talitos",
+		.blocksize = AES_BLOCK_SIZE,
+		.aead = {
+			.setkey = aead_authenc_setkey,
+			.setauthsize = aead_authenc_setauthsize,
+			.encrypt = aead_authenc_encrypt,
+			.decrypt = aead_authenc_decrypt,
+			.givencrypt = aead_authenc_givencrypt,
+			.geniv = "<built-in>",
+			.ivsize = AES_BLOCK_SIZE,
+			.maxauthsize = SHA1_DIGEST_SIZE,
+			},
+		.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
+			             DESC_HDR_SEL0_AESU |
+		                     DESC_HDR_MODE0_AESU_CBC |
+		                     DESC_HDR_SEL1_MDEUA |
+		                     DESC_HDR_MODE1_MDEU_INIT |
+		                     DESC_HDR_MODE1_MDEU_PAD |
+		                     DESC_HDR_MODE1_MDEU_SHA1_HMAC,
+	},
+	{
+		.name = "authenc(hmac(sha1),cbc(des3_ede))",
+		.driver_name = "authenc-hmac-sha1-cbc-3des-talitos",
+		.blocksize = DES3_EDE_BLOCK_SIZE,
+		.aead = {
+			.setkey = aead_authenc_setkey,
+			.setauthsize = aead_authenc_setauthsize,
+			.encrypt = aead_authenc_encrypt,
+			.decrypt = aead_authenc_decrypt,
+			.givencrypt = aead_authenc_givencrypt,
+			.geniv = "<built-in>",
+			.ivsize = DES3_EDE_BLOCK_SIZE,
+			.maxauthsize = SHA1_DIGEST_SIZE,
+			},
+		.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
+			             DESC_HDR_SEL0_DEU |
+		                     DESC_HDR_MODE0_DEU_CBC |
+		                     DESC_HDR_MODE0_DEU_3DES |
+		                     DESC_HDR_SEL1_MDEUA |
+		                     DESC_HDR_MODE1_MDEU_INIT |
+		                     DESC_HDR_MODE1_MDEU_PAD |
+		                     DESC_HDR_MODE1_MDEU_SHA1_HMAC,
+	},
+	{
+		.name = "authenc(hmac(sha256),cbc(aes))",
+		.driver_name = "authenc-hmac-sha256-cbc-aes-talitos",
+		.blocksize = AES_BLOCK_SIZE,
+		.aead = {
+			.setkey = aead_authenc_setkey,
+			.setauthsize = aead_authenc_setauthsize,
+			.encrypt = aead_authenc_encrypt,
+			.decrypt = aead_authenc_decrypt,
+			.givencrypt = aead_authenc_givencrypt,
+			.geniv = "<built-in>",
+			.ivsize = AES_BLOCK_SIZE,
+			.maxauthsize = SHA256_DIGEST_SIZE,
+			},
+		.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
+			             DESC_HDR_SEL0_AESU |
+		                     DESC_HDR_MODE0_AESU_CBC |
+		                     DESC_HDR_SEL1_MDEUA |
+		                     DESC_HDR_MODE1_MDEU_INIT |
+		                     DESC_HDR_MODE1_MDEU_PAD |
+		                     DESC_HDR_MODE1_MDEU_SHA256_HMAC,
+	},
+	{
+		.name = "authenc(hmac(sha256),cbc(des3_ede))",
+		.driver_name = "authenc-hmac-sha256-cbc-3des-talitos",
+		.blocksize = DES3_EDE_BLOCK_SIZE,
+		.aead = {
+			.setkey = aead_authenc_setkey,
+			.setauthsize = aead_authenc_setauthsize,
+			.encrypt = aead_authenc_encrypt,
+			.decrypt = aead_authenc_decrypt,
+			.givencrypt = aead_authenc_givencrypt,
+			.geniv = "<built-in>",
+			.ivsize = DES3_EDE_BLOCK_SIZE,
+			.maxauthsize = SHA256_DIGEST_SIZE,
+			},
+		.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
+			             DESC_HDR_SEL0_DEU |
+		                     DESC_HDR_MODE0_DEU_CBC |
+		                     DESC_HDR_MODE0_DEU_3DES |
+		                     DESC_HDR_SEL1_MDEUA |
+		                     DESC_HDR_MODE1_MDEU_INIT |
+		                     DESC_HDR_MODE1_MDEU_PAD |
+		                     DESC_HDR_MODE1_MDEU_SHA256_HMAC,
+	},
+	{
+		.name = "authenc(hmac(md5),cbc(aes))",
+		.driver_name = "authenc-hmac-md5-cbc-aes-talitos",
+		.blocksize = AES_BLOCK_SIZE,
+		.aead = {
+			.setkey = aead_authenc_setkey,
+			.setauthsize = aead_authenc_setauthsize,
+			.encrypt = aead_authenc_encrypt,
+			.decrypt = aead_authenc_decrypt,
+			.givencrypt = aead_authenc_givencrypt,
+			.geniv = "<built-in>",
+			.ivsize = AES_BLOCK_SIZE,
+			.maxauthsize = MD5_DIGEST_SIZE,
+			},
+		.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
+			             DESC_HDR_SEL0_AESU |
+		                     DESC_HDR_MODE0_AESU_CBC |
+		                     DESC_HDR_SEL1_MDEUA |
+		                     DESC_HDR_MODE1_MDEU_INIT |
+		                     DESC_HDR_MODE1_MDEU_PAD |
+		                     DESC_HDR_MODE1_MDEU_MD5_HMAC,
+	},
+	{
+		.name = "authenc(hmac(md5),cbc(des3_ede))",
+		.driver_name = "authenc-hmac-md5-cbc-3des-talitos",
+		.blocksize = DES3_EDE_BLOCK_SIZE,
+		.aead = {
+			.setkey = aead_authenc_setkey,
+			.setauthsize = aead_authenc_setauthsize,
+			.encrypt = aead_authenc_encrypt,
+			.decrypt = aead_authenc_decrypt,
+			.givencrypt = aead_authenc_givencrypt,
+			.geniv = "<built-in>",
+			.ivsize = DES3_EDE_BLOCK_SIZE,
+			.maxauthsize = MD5_DIGEST_SIZE,
+			},
+		.desc_hdr_template = DESC_HDR_TYPE_IPSEC_ESP |
+			             DESC_HDR_SEL0_DEU |
+		                     DESC_HDR_MODE0_DEU_CBC |
+		                     DESC_HDR_MODE0_DEU_3DES |
+		                     DESC_HDR_SEL1_MDEUA |
+		                     DESC_HDR_MODE1_MDEU_INIT |
+		                     DESC_HDR_MODE1_MDEU_PAD |
+		                     DESC_HDR_MODE1_MDEU_MD5_HMAC,
+	}
+};
+
+struct talitos_crypto_alg {
+	struct list_head entry;
+	struct device *dev;
+	__be32 desc_hdr_template;
+	struct crypto_alg crypto_alg;
+};
+
+static int talitos_cra_init(struct crypto_tfm *tfm)
+{
+	struct crypto_alg *alg = tfm->__crt_alg;
+	struct talitos_crypto_alg *talitos_alg =
+		 container_of(alg, struct talitos_crypto_alg, crypto_alg);
+	struct talitos_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	/* update context with ptr to dev */
+	ctx->dev = talitos_alg->dev;
+	/* copy descriptor header template value */
+	ctx->desc_hdr_template = talitos_alg->desc_hdr_template;
+
+	/* random first IV */
+	get_random_bytes(ctx->iv, TALITOS_MAX_IV_LENGTH);
+
+	return 0;
+}
+
+/*
+ * given the alg's descriptor header template, determine whether descriptor
+ * type and primary/secondary execution units required match the hw
+ * capabilities description provided in the device tree node.
+ */
+static int hw_supports(struct device *dev, __be32 desc_hdr_template)
+{
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	int ret;
+
+	ret = (1 << DESC_TYPE(desc_hdr_template) & priv->desc_types) &&
+	      (1 << PRIMARY_EU(desc_hdr_template) & priv->exec_units);
+
+	if (SECONDARY_EU(desc_hdr_template))
+		ret = ret && (1 << SECONDARY_EU(desc_hdr_template)
+		              & priv->exec_units);
+
+	return ret;
+}
+
+static int __devexit talitos_remove(struct of_device *ofdev)
+{
+	struct device *dev = &ofdev->dev;
+	struct talitos_private *priv = dev_get_drvdata(dev);
+	struct talitos_crypto_alg *t_alg, *n;
+	int i;
+
+	list_for_each_entry_safe(t_alg, n, &priv->alg_list, entry) {
+		crypto_unregister_alg(&t_alg->crypto_alg);
+		list_del(&t_alg->entry);
+		kfree(t_alg);
+	}
+
+	if (hw_supports(dev, DESC_HDR_SEL0_RNG))
+		talitos_unregister_rng(dev);
+
+	kfree(priv->tail);
+	kfree(priv->head);
+
+	if (priv->fifo)
+		for (i = 0; i < priv->num_channels; i++)
+			kfree(priv->fifo[i]);
+
+	kfree(priv->fifo);
+	kfree(priv->head_lock);
+	kfree(priv->tail_lock);
+
+	if (priv->irq != NO_IRQ) {
+		free_irq(priv->irq, dev);
+		irq_dispose_mapping(priv->irq);
+	}
+
+	tasklet_kill(&priv->done_task);
+	tasklet_kill(&priv->error_task);
+
+	iounmap(priv->reg);
+
+	dev_set_drvdata(dev, NULL);
+
+	kfree(priv);
+
+	return 0;
+}
+
+static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
+						    struct talitos_alg_template
+						           *template)
+{
+	struct talitos_crypto_alg *t_alg;
+	struct crypto_alg *alg;
+
+	t_alg = kzalloc(sizeof(struct talitos_crypto_alg), GFP_KERNEL);
+	if (!t_alg)
+		return ERR_PTR(-ENOMEM);
+
+	alg = &t_alg->crypto_alg;
+
+	snprintf(alg->cra_name, CRYPTO_MAX_ALG_NAME, "%s", template->name);
+	snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
+		 template->driver_name);
+	alg->cra_module = THIS_MODULE;
+	alg->cra_init = talitos_cra_init;
+	alg->cra_priority = TALITOS_CRA_PRIORITY;
+	alg->cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
+	alg->cra_blocksize = template->blocksize;
+	alg->cra_alignmask = 0;
+	alg->cra_type = &crypto_aead_type;
+	alg->cra_ctxsize = sizeof(struct talitos_ctx);
+	alg->cra_u.aead = template->aead;
+
+	t_alg->desc_hdr_template = template->desc_hdr_template;
+	t_alg->dev = dev;
+
+	return t_alg;
+}
+
+static int talitos_probe(struct of_device *ofdev,
+			 const struct of_device_id *match)
+{
+	struct device *dev = &ofdev->dev;
+	struct device_node *np = ofdev->node;
+	struct talitos_private *priv;
+	const unsigned int *prop;
+	int i, err;
+
+	priv = kzalloc(sizeof(struct talitos_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+
+	priv->ofdev = ofdev;
+
+	tasklet_init(&priv->done_task, talitos_done, (unsigned long)dev);
+	tasklet_init(&priv->error_task, talitos_error, (unsigned long)dev);
+
+	priv->irq = irq_of_parse_and_map(np, 0);
+
+	if (priv->irq == NO_IRQ) {
+		dev_err(dev, "failed to map irq\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	/* get the irq line */
+	err = request_irq(priv->irq, talitos_interrupt, 0,
+			  dev_driver_string(dev), dev);
+	if (err) {
+		dev_err(dev, "failed to request irq %d\n", priv->irq);
+		irq_dispose_mapping(priv->irq);
+		priv->irq = NO_IRQ;
+		goto err_out;
+	}
+
+	priv->reg = of_iomap(np, 0);
+	if (!priv->reg) {
+		dev_err(dev, "failed to of_iomap\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	/* get SEC version capabilities from device tree */
+	prop = of_get_property(np, "fsl,num-channels", NULL);
+	if (prop)
+		priv->num_channels = *prop;
+
+	prop = of_get_property(np, "fsl,channel-fifo-len", NULL);
+	if (prop)
+		priv->chfifo_len = *prop;
+
+	prop = of_get_property(np, "fsl,exec-units-mask", NULL);
+	if (prop)
+		priv->exec_units = *prop;
+
+	prop = of_get_property(np, "fsl,descriptor-types-mask", NULL);
+	if (prop)
+		priv->desc_types = *prop;
+
+	if (!is_power_of_2(priv->num_channels) || !priv->chfifo_len ||
+	    !priv->exec_units || !priv->desc_types) {
+		dev_err(dev, "invalid property data in device tree node\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	of_node_put(np);
+	np = NULL;
+
+	priv->head_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
+				  GFP_KERNEL);
+	priv->tail_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
+				  GFP_KERNEL);
+	if (!priv->head_lock || !priv->tail_lock) {
+		dev_err(dev, "failed to allocate fifo locks\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	for (i = 0; i < priv->num_channels; i++) {
+		spin_lock_init(&priv->head_lock[i]);
+		spin_lock_init(&priv->tail_lock[i]);
+	}
+
+	priv->fifo = kmalloc(sizeof(struct talitos_request *) *
+			     priv->num_channels, GFP_KERNEL);
+	if (!priv->fifo) {
+		dev_err(dev, "failed to allocate request fifo\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	priv->fifo_len = roundup_pow_of_two(priv->chfifo_len);
+
+	for (i = 0; i < priv->num_channels; i++) {
+		priv->fifo[i] = kzalloc(sizeof(struct talitos_request) *
+					priv->fifo_len, GFP_KERNEL);
+		if (!priv->fifo[i]) {
+			dev_err(dev, "failed to allocate request fifo %d\n", i);
+			err = -ENOMEM;
+			goto err_out;
+		}
+	}
+
+	priv->head = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
+	priv->tail = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
+	if (!priv->head || !priv->tail) {
+		dev_err(dev, "failed to allocate request index space\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	/* reset and initialize the h/w */
+	err = init_device(dev);
+	if (err) {
+		dev_err(dev, "failed to initialize device\n");
+		goto err_out;
+	}
+
+	/* register the RNG, if available */
+	if (hw_supports(dev, DESC_HDR_SEL0_RNG)) {
+		err = talitos_register_rng(dev);
+		if (err) {
+			dev_err(dev, "failed to register hwrng: %d\n", err);
+			goto err_out;
+		} else
+			dev_info(dev, "hwrng\n");
+	}
+
+	/* register crypto algorithms the device supports */
+	INIT_LIST_HEAD(&priv->alg_list);
+
+	for (i = 0; i < ARRAY_SIZE(driver_algs); i++) {
+		if (hw_supports(dev, driver_algs[i].desc_hdr_template)) {
+			struct talitos_crypto_alg *t_alg;
+
+			t_alg = talitos_alg_alloc(dev, &driver_algs[i]);
+			if (IS_ERR(t_alg)) {
+				err = PTR_ERR(t_alg);
+				goto err_out;
+			}
+
+			err = crypto_register_alg(&t_alg->crypto_alg);
+			if (err) {
+				dev_err(dev, "%s alg registration failed\n",
+					t_alg->crypto_alg.cra_driver_name);
+				kfree(t_alg);
+			} else {
+				list_add_tail(&t_alg->entry, &priv->alg_list);
+				dev_info(dev, "%s\n",
+					 t_alg->crypto_alg.cra_driver_name);
+			}
+		}
+	}
+
+	return 0;
+
+err_out:
+	talitos_remove(ofdev);
+	if (np)
+		of_node_put(np);
+
+	return err;
+}
+
+static struct of_device_id talitos_match[] = {
+	{
+		.compatible = "fsl,sec2.0",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, talitos_match);
+
+static struct of_platform_driver talitos_driver = {
+	.name = "talitos",
+	.match_table = talitos_match,
+	.probe = talitos_probe,
+	.remove = __devexit_p(talitos_remove),
+};
+
+static int __init talitos_init(void)
+{
+	return of_register_platform_driver(&talitos_driver);
+}
+module_init(talitos_init);
+
+static void __exit talitos_exit(void)
+{
+	of_unregister_platform_driver(&talitos_driver);
+}
+module_exit(talitos_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kim Phillips <kim.phillips@freescale.com>");
+MODULE_DESCRIPTION("Freescale integrated security engine (SEC) driver");
diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h
new file mode 100644
index 0000000..c48a405
--- /dev/null
+++ b/drivers/crypto/talitos.h
@@ -0,0 +1,199 @@
+/*
+ * Freescale SEC (talitos) device register and descriptor header defines
+ *
+ * Copyright (c) 2006-2008 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * TALITOS_xxx_LO addresses point to the low data bits (32-63) of the register
+ */
+
+/* global register offset addresses */
+#define TALITOS_MCR			0x1030  /* master control register */
+#define TALITOS_MCR_LO			0x1038
+#define   TALITOS_MCR_SWR		0x1     /* s/w reset */
+#define TALITOS_IMR			0x1008  /* interrupt mask register */
+#define   TALITOS_IMR_INIT		0x10fff /* enable channel IRQs */
+#define TALITOS_IMR_LO			0x100C
+#define   TALITOS_IMR_LO_INIT		0x20000 /* allow RNGU error IRQs */
+#define TALITOS_ISR			0x1010  /* interrupt status register */
+#define   TALITOS_ISR_CHERR		0xaa    /* channel errors mask */
+#define   TALITOS_ISR_CHDONE		0x55    /* channel done mask */
+#define TALITOS_ISR_LO			0x1014
+#define TALITOS_ICR			0x1018  /* interrupt clear register */
+#define TALITOS_ICR_LO			0x101C
+
+/* channel register address stride */
+#define TALITOS_CH_STRIDE		0x100
+
+/* channel configuration register  */
+#define TALITOS_CCCR(ch)		(ch * TALITOS_CH_STRIDE + 0x1108)
+#define   TALITOS_CCCR_CONT		0x2    /* channel continue */
+#define   TALITOS_CCCR_RESET		0x1    /* channel reset */
+#define TALITOS_CCCR_LO(ch)		(ch * TALITOS_CH_STRIDE + 0x110c)
+#define   TALITOS_CCCR_LO_CDWE		0x10   /* chan. done writeback enab. */
+#define   TALITOS_CCCR_LO_NT		0x4    /* notification type */
+#define   TALITOS_CCCR_LO_CDIE		0x2    /* channel done IRQ enable */
+
+/* CCPSR: channel pointer status register */
+#define TALITOS_CCPSR(ch)		(ch * TALITOS_CH_STRIDE + 0x1110)
+#define TALITOS_CCPSR_LO(ch)		(ch * TALITOS_CH_STRIDE + 0x1114)
+#define   TALITOS_CCPSR_LO_DOF		0x8000 /* double FF write oflow error */
+#define   TALITOS_CCPSR_LO_SOF		0x4000 /* single FF write oflow error */
+#define   TALITOS_CCPSR_LO_MDTE		0x2000 /* master data transfer error */
+#define   TALITOS_CCPSR_LO_SGDLZ	0x1000 /* s/g data len zero error */
+#define   TALITOS_CCPSR_LO_FPZ		0x0800 /* fetch ptr zero error */
+#define   TALITOS_CCPSR_LO_IDH		0x0400 /* illegal desc hdr error */
+#define   TALITOS_CCPSR_LO_IEU		0x0200 /* invalid EU error */
+#define   TALITOS_CCPSR_LO_EU		0x0100 /* EU error detected */
+#define   TALITOS_CCPSR_LO_GB		0x0080 /* gather boundary error */
+#define   TALITOS_CCPSR_LO_GRL		0x0040 /* gather return/length error */
+#define   TALITOS_CCPSR_LO_SB		0x0020 /* scatter boundary error */
+#define   TALITOS_CCPSR_LO_SRL		0x0010 /* scatter return/length error */
+
+/* channel fetch fifo register */
+#define TALITOS_FF(ch)			(ch * TALITOS_CH_STRIDE + 0x1148)
+#define TALITOS_FF_LO(ch)		(ch * TALITOS_CH_STRIDE + 0x114c)
+
+/* current descriptor pointer register */
+#define TALITOS_CDPR(ch)		(ch * TALITOS_CH_STRIDE + 0x1140)
+#define TALITOS_CDPR_LO(ch)		(ch * TALITOS_CH_STRIDE + 0x1144)
+
+/* descriptor buffer register */
+#define TALITOS_DESCBUF(ch)		(ch * TALITOS_CH_STRIDE + 0x1180)
+#define TALITOS_DESCBUF_LO(ch)		(ch * TALITOS_CH_STRIDE + 0x1184)
+
+/* gather link table */
+#define TALITOS_GATHER(ch)		(ch * TALITOS_CH_STRIDE + 0x11c0)
+#define TALITOS_GATHER_LO(ch)		(ch * TALITOS_CH_STRIDE + 0x11c4)
+
+/* scatter link table */
+#define TALITOS_SCATTER(ch)		(ch * TALITOS_CH_STRIDE + 0x11e0)
+#define TALITOS_SCATTER_LO(ch)		(ch * TALITOS_CH_STRIDE + 0x11e4)
+
+/* execution unit interrupt status registers */
+#define TALITOS_DEUISR			0x2030 /* DES unit */
+#define TALITOS_DEUISR_LO		0x2034
+#define TALITOS_AESUISR			0x4030 /* AES unit */
+#define TALITOS_AESUISR_LO		0x4034
+#define TALITOS_MDEUISR			0x6030 /* message digest unit */
+#define TALITOS_MDEUISR_LO		0x6034
+#define TALITOS_AFEUISR			0x8030 /* arc4 unit */
+#define TALITOS_AFEUISR_LO		0x8034
+#define TALITOS_RNGUISR			0xa030 /* random number unit */
+#define TALITOS_RNGUISR_LO		0xa034
+#define TALITOS_RNGUSR			0xa028 /* rng status */
+#define TALITOS_RNGUSR_LO		0xa02c
+#define   TALITOS_RNGUSR_LO_RD		0x1	/* reset done */
+#define   TALITOS_RNGUSR_LO_OFL		0xff0000/* output FIFO length */
+#define TALITOS_RNGUDSR			0xa010	/* data size */
+#define TALITOS_RNGUDSR_LO		0xa014
+#define TALITOS_RNGU_FIFO		0xa800	/* output FIFO */
+#define TALITOS_RNGU_FIFO_LO		0xa804	/* output FIFO */
+#define TALITOS_RNGURCR			0xa018	/* reset control */
+#define TALITOS_RNGURCR_LO		0xa01c
+#define   TALITOS_RNGURCR_LO_SR		0x1	/* software reset */
+#define TALITOS_PKEUISR			0xc030 /* public key unit */
+#define TALITOS_PKEUISR_LO		0xc034
+#define TALITOS_KEUISR			0xe030 /* kasumi unit */
+#define TALITOS_KEUISR_LO		0xe034
+#define TALITOS_CRCUISR			0xf030 /* cyclic redundancy check unit*/
+#define TALITOS_CRCUISR_LO		0xf034
+
+/*
+ * talitos descriptor header (hdr) bits
+ */
+
+/* written back when done */
+#define DESC_HDR_DONE			__constant_cpu_to_be32(0xff000000)
+
+/* primary execution unit select */
+#define	DESC_HDR_SEL0_MASK		__constant_cpu_to_be32(0xf0000000)
+#define	DESC_HDR_SEL0_AFEU		__constant_cpu_to_be32(0x10000000)
+#define	DESC_HDR_SEL0_DEU		__constant_cpu_to_be32(0x20000000)
+#define	DESC_HDR_SEL0_MDEUA		__constant_cpu_to_be32(0x30000000)
+#define	DESC_HDR_SEL0_MDEUB		__constant_cpu_to_be32(0xb0000000)
+#define	DESC_HDR_SEL0_RNG		__constant_cpu_to_be32(0x40000000)
+#define	DESC_HDR_SEL0_PKEU		__constant_cpu_to_be32(0x50000000)
+#define	DESC_HDR_SEL0_AESU		__constant_cpu_to_be32(0x60000000)
+#define	DESC_HDR_SEL0_KEU		__constant_cpu_to_be32(0x70000000)
+#define	DESC_HDR_SEL0_CRCU		__constant_cpu_to_be32(0x80000000)
+
+/* primary execution unit mode (MODE0) and derivatives */
+#define	DESC_HDR_MODE0_ENCRYPT		__constant_cpu_to_be32(0x00100000)
+#define	DESC_HDR_MODE0_AESU_CBC		__constant_cpu_to_be32(0x00200000)
+#define	DESC_HDR_MODE0_DEU_CBC		__constant_cpu_to_be32(0x00400000)
+#define	DESC_HDR_MODE0_DEU_3DES		__constant_cpu_to_be32(0x00200000)
+#define	DESC_HDR_MODE0_MDEU_INIT	__constant_cpu_to_be32(0x01000000)
+#define	DESC_HDR_MODE0_MDEU_HMAC	__constant_cpu_to_be32(0x00800000)
+#define	DESC_HDR_MODE0_MDEU_PAD		__constant_cpu_to_be32(0x00400000)
+#define	DESC_HDR_MODE0_MDEU_MD5		__constant_cpu_to_be32(0x00200000)
+#define	DESC_HDR_MODE0_MDEU_SHA256	__constant_cpu_to_be32(0x00100000)
+#define	DESC_HDR_MODE0_MDEU_SHA1	__constant_cpu_to_be32(0x00000000)
+#define	DESC_HDR_MODE0_MDEU_MD5_HMAC	(DESC_HDR_MODE0_MDEU_MD5 | \
+					 DESC_HDR_MODE0_MDEU_HMAC)
+#define	DESC_HDR_MODE0_MDEU_SHA256_HMAC	(DESC_HDR_MODE0_MDEU_SHA256 | \
+					 DESC_HDR_MODE0_MDEU_HMAC)
+#define	DESC_HDR_MODE0_MDEU_SHA1_HMAC	(DESC_HDR_MODE0_MDEU_SHA1 | \
+					 DESC_HDR_MODE0_MDEU_HMAC)
+
+/* secondary execution unit select (SEL1) */
+#define	DESC_HDR_SEL1_MASK		__constant_cpu_to_be32(0x000f0000)
+#define	DESC_HDR_SEL1_MDEUA		__constant_cpu_to_be32(0x00030000)
+#define	DESC_HDR_SEL1_MDEUB		__constant_cpu_to_be32(0x000b0000)
+#define	DESC_HDR_SEL1_CRCU		__constant_cpu_to_be32(0x00080000)
+
+/* secondary execution unit mode (MODE1) and derivatives */
+#define	DESC_HDR_MODE1_MDEU_INIT	__constant_cpu_to_be32(0x00001000)
+#define	DESC_HDR_MODE1_MDEU_HMAC	__constant_cpu_to_be32(0x00000800)
+#define	DESC_HDR_MODE1_MDEU_PAD		__constant_cpu_to_be32(0x00000400)
+#define	DESC_HDR_MODE1_MDEU_MD5		__constant_cpu_to_be32(0x00000200)
+#define	DESC_HDR_MODE1_MDEU_SHA256	__constant_cpu_to_be32(0x00000100)
+#define	DESC_HDR_MODE1_MDEU_SHA1	__constant_cpu_to_be32(0x00000000)
+#define	DESC_HDR_MODE1_MDEU_MD5_HMAC	(DESC_HDR_MODE1_MDEU_MD5 | \
+					 DESC_HDR_MODE1_MDEU_HMAC)
+#define	DESC_HDR_MODE1_MDEU_SHA256_HMAC	(DESC_HDR_MODE1_MDEU_SHA256 | \
+					 DESC_HDR_MODE1_MDEU_HMAC)
+#define	DESC_HDR_MODE1_MDEU_SHA1_HMAC	(DESC_HDR_MODE1_MDEU_SHA1 | \
+					 DESC_HDR_MODE1_MDEU_HMAC)
+
+/* direction of overall data flow (DIR) */
+#define	DESC_HDR_DIR_INBOUND		__constant_cpu_to_be32(0x00000002)
+
+/* request done notification (DN) */
+#define	DESC_HDR_DONE_NOTIFY		__constant_cpu_to_be32(0x00000001)
+
+/* descriptor types */
+#define DESC_HDR_TYPE_AESU_CTR_NONSNOOP		__constant_cpu_to_be32(0 << 3)
+#define DESC_HDR_TYPE_IPSEC_ESP			__constant_cpu_to_be32(1 << 3)
+#define DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU	__constant_cpu_to_be32(2 << 3)
+#define DESC_HDR_TYPE_HMAC_SNOOP_NO_AFEU	__constant_cpu_to_be32(4 << 3)
+
+/* link table extent field bits */
+#define DESC_PTR_LNKTBL_JUMP			0x80
+#define DESC_PTR_LNKTBL_RETURN			0x02
+#define DESC_PTR_LNKTBL_NEXT			0x01
diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
new file mode 100644
index 0000000..de566cf
--- /dev/null
+++ b/drivers/gpu/Makefile
@@ -0,0 +1 @@
+obj-y			+= drm/
diff --git a/drivers/char/drm/Kconfig b/drivers/gpu/drm/Kconfig
similarity index 100%
rename from drivers/char/drm/Kconfig
rename to drivers/gpu/drm/Kconfig
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
new file mode 100644
index 0000000..e9f9a97
--- /dev/null
+++ b/drivers/gpu/drm/Makefile
@@ -0,0 +1,26 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+
+drm-y       :=	drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
+		drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \
+		drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
+		drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
+		drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o
+
+drm-$(CONFIG_COMPAT) += drm_ioc32.o
+
+obj-$(CONFIG_DRM)	+= drm.o
+obj-$(CONFIG_DRM_TDFX)	+= tdfx/
+obj-$(CONFIG_DRM_R128)	+= r128/
+obj-$(CONFIG_DRM_RADEON)+= radeon/
+obj-$(CONFIG_DRM_MGA)	+= mga/
+obj-$(CONFIG_DRM_I810)	+= i810/
+obj-$(CONFIG_DRM_I830)	+= i830/
+obj-$(CONFIG_DRM_I915)  += i915/
+obj-$(CONFIG_DRM_SIS)   += sis/
+obj-$(CONFIG_DRM_SAVAGE)+= savage/
+obj-$(CONFIG_DRM_VIA)	+=via/
+
diff --git a/drivers/char/drm/README.drm b/drivers/gpu/drm/README.drm
similarity index 100%
rename from drivers/char/drm/README.drm
rename to drivers/gpu/drm/README.drm
diff --git a/drivers/char/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c
similarity index 100%
rename from drivers/char/drm/ati_pcigart.c
rename to drivers/gpu/drm/ati_pcigart.c
diff --git a/drivers/char/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c
similarity index 100%
rename from drivers/char/drm/drm_agpsupport.c
rename to drivers/gpu/drm/drm_agpsupport.c
diff --git a/drivers/char/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
similarity index 100%
rename from drivers/char/drm/drm_auth.c
rename to drivers/gpu/drm/drm_auth.c
diff --git a/drivers/char/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
similarity index 100%
rename from drivers/char/drm/drm_bufs.c
rename to drivers/gpu/drm/drm_bufs.c
diff --git a/drivers/char/drm/drm_context.c b/drivers/gpu/drm/drm_context.c
similarity index 100%
rename from drivers/char/drm/drm_context.c
rename to drivers/gpu/drm/drm_context.c
diff --git a/drivers/char/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c
similarity index 100%
rename from drivers/char/drm/drm_dma.c
rename to drivers/gpu/drm/drm_dma.c
diff --git a/drivers/char/drm/drm_drawable.c b/drivers/gpu/drm/drm_drawable.c
similarity index 100%
rename from drivers/char/drm/drm_drawable.c
rename to drivers/gpu/drm/drm_drawable.c
diff --git a/drivers/char/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
similarity index 100%
rename from drivers/char/drm/drm_drv.c
rename to drivers/gpu/drm/drm_drv.c
diff --git a/drivers/char/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
similarity index 100%
rename from drivers/char/drm/drm_fops.c
rename to drivers/gpu/drm/drm_fops.c
diff --git a/drivers/char/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c
similarity index 100%
rename from drivers/char/drm/drm_hashtab.c
rename to drivers/gpu/drm/drm_hashtab.c
diff --git a/drivers/char/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
similarity index 100%
rename from drivers/char/drm/drm_ioc32.c
rename to drivers/gpu/drm/drm_ioc32.c
diff --git a/drivers/char/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
similarity index 100%
rename from drivers/char/drm/drm_ioctl.c
rename to drivers/gpu/drm/drm_ioctl.c
diff --git a/drivers/char/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
similarity index 100%
rename from drivers/char/drm/drm_irq.c
rename to drivers/gpu/drm/drm_irq.c
diff --git a/drivers/char/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
similarity index 100%
rename from drivers/char/drm/drm_lock.c
rename to drivers/gpu/drm/drm_lock.c
diff --git a/drivers/char/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c
similarity index 100%
rename from drivers/char/drm/drm_memory.c
rename to drivers/gpu/drm/drm_memory.c
diff --git a/drivers/char/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
similarity index 100%
rename from drivers/char/drm/drm_mm.c
rename to drivers/gpu/drm/drm_mm.c
diff --git a/drivers/char/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
similarity index 100%
rename from drivers/char/drm/drm_pci.c
rename to drivers/gpu/drm/drm_pci.c
diff --git a/drivers/char/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c
similarity index 100%
rename from drivers/char/drm/drm_proc.c
rename to drivers/gpu/drm/drm_proc.c
diff --git a/drivers/char/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c
similarity index 100%
rename from drivers/char/drm/drm_scatter.c
rename to drivers/gpu/drm/drm_scatter.c
diff --git a/drivers/char/drm/drm_sman.c b/drivers/gpu/drm/drm_sman.c
similarity index 100%
rename from drivers/char/drm/drm_sman.c
rename to drivers/gpu/drm/drm_sman.c
diff --git a/drivers/char/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
similarity index 100%
rename from drivers/char/drm/drm_stub.c
rename to drivers/gpu/drm/drm_stub.c
diff --git a/drivers/char/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
similarity index 100%
rename from drivers/char/drm/drm_sysfs.c
rename to drivers/gpu/drm/drm_sysfs.c
diff --git a/drivers/char/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
similarity index 100%
rename from drivers/char/drm/drm_vm.c
rename to drivers/gpu/drm/drm_vm.c
diff --git a/drivers/gpu/drm/i810/Makefile b/drivers/gpu/drm/i810/Makefile
new file mode 100644
index 0000000..43844ec
--- /dev/null
+++ b/drivers/gpu/drm/i810/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+i810-y := i810_drv.o i810_dma.o
+
+obj-$(CONFIG_DRM_I810)	+= i810.o
diff --git a/drivers/char/drm/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
similarity index 100%
rename from drivers/char/drm/i810_dma.c
rename to drivers/gpu/drm/i810/i810_dma.c
diff --git a/drivers/char/drm/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
similarity index 100%
rename from drivers/char/drm/i810_drv.c
rename to drivers/gpu/drm/i810/i810_drv.c
diff --git a/drivers/char/drm/i810_drv.h b/drivers/gpu/drm/i810/i810_drv.h
similarity index 100%
rename from drivers/char/drm/i810_drv.h
rename to drivers/gpu/drm/i810/i810_drv.h
diff --git a/drivers/gpu/drm/i830/Makefile b/drivers/gpu/drm/i830/Makefile
new file mode 100644
index 0000000..c642ee0
--- /dev/null
+++ b/drivers/gpu/drm/i830/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+i830-y := i830_drv.o i830_dma.o i830_irq.o
+
+obj-$(CONFIG_DRM_I830) += i830.o
diff --git a/drivers/char/drm/i830_dma.c b/drivers/gpu/drm/i830/i830_dma.c
similarity index 100%
rename from drivers/char/drm/i830_dma.c
rename to drivers/gpu/drm/i830/i830_dma.c
diff --git a/drivers/char/drm/i830_drv.c b/drivers/gpu/drm/i830/i830_drv.c
similarity index 100%
rename from drivers/char/drm/i830_drv.c
rename to drivers/gpu/drm/i830/i830_drv.c
diff --git a/drivers/char/drm/i830_drv.h b/drivers/gpu/drm/i830/i830_drv.h
similarity index 100%
rename from drivers/char/drm/i830_drv.h
rename to drivers/gpu/drm/i830/i830_drv.h
diff --git a/drivers/char/drm/i830_irq.c b/drivers/gpu/drm/i830/i830_irq.c
similarity index 100%
rename from drivers/char/drm/i830_irq.c
rename to drivers/gpu/drm/i830/i830_irq.c
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
new file mode 100644
index 0000000..a9e6046
--- /dev/null
+++ b/drivers/gpu/drm/i915/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o
+
+i915-$(CONFIG_COMPAT)   += i915_ioc32.o
+
+obj-$(CONFIG_DRM_I915)  += i915.o
diff --git a/drivers/char/drm/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
similarity index 100%
rename from drivers/char/drm/i915_dma.c
rename to drivers/gpu/drm/i915/i915_dma.c
diff --git a/drivers/char/drm/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
similarity index 100%
rename from drivers/char/drm/i915_drv.c
rename to drivers/gpu/drm/i915/i915_drv.c
diff --git a/drivers/char/drm/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
similarity index 100%
rename from drivers/char/drm/i915_drv.h
rename to drivers/gpu/drm/i915/i915_drv.h
diff --git a/drivers/char/drm/i915_ioc32.c b/drivers/gpu/drm/i915/i915_ioc32.c
similarity index 100%
rename from drivers/char/drm/i915_ioc32.c
rename to drivers/gpu/drm/i915/i915_ioc32.c
diff --git a/drivers/char/drm/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
similarity index 100%
rename from drivers/char/drm/i915_irq.c
rename to drivers/gpu/drm/i915/i915_irq.c
diff --git a/drivers/char/drm/i915_mem.c b/drivers/gpu/drm/i915/i915_mem.c
similarity index 100%
rename from drivers/char/drm/i915_mem.c
rename to drivers/gpu/drm/i915/i915_mem.c
diff --git a/drivers/gpu/drm/mga/Makefile b/drivers/gpu/drm/mga/Makefile
new file mode 100644
index 0000000..6068478
--- /dev/null
+++ b/drivers/gpu/drm/mga/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+mga-y := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
+
+mga-$(CONFIG_COMPAT) += mga_ioc32.o
+
+obj-$(CONFIG_DRM_MGA)	+= mga.o
+
diff --git a/drivers/char/drm/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c
similarity index 100%
rename from drivers/char/drm/mga_dma.c
rename to drivers/gpu/drm/mga/mga_dma.c
diff --git a/drivers/char/drm/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
similarity index 100%
rename from drivers/char/drm/mga_drv.c
rename to drivers/gpu/drm/mga/mga_drv.c
diff --git a/drivers/char/drm/mga_drv.h b/drivers/gpu/drm/mga/mga_drv.h
similarity index 100%
rename from drivers/char/drm/mga_drv.h
rename to drivers/gpu/drm/mga/mga_drv.h
diff --git a/drivers/char/drm/mga_ioc32.c b/drivers/gpu/drm/mga/mga_ioc32.c
similarity index 100%
rename from drivers/char/drm/mga_ioc32.c
rename to drivers/gpu/drm/mga/mga_ioc32.c
diff --git a/drivers/char/drm/mga_irq.c b/drivers/gpu/drm/mga/mga_irq.c
similarity index 100%
rename from drivers/char/drm/mga_irq.c
rename to drivers/gpu/drm/mga/mga_irq.c
diff --git a/drivers/char/drm/mga_state.c b/drivers/gpu/drm/mga/mga_state.c
similarity index 100%
rename from drivers/char/drm/mga_state.c
rename to drivers/gpu/drm/mga/mga_state.c
diff --git a/drivers/char/drm/mga_ucode.h b/drivers/gpu/drm/mga/mga_ucode.h
similarity index 100%
rename from drivers/char/drm/mga_ucode.h
rename to drivers/gpu/drm/mga/mga_ucode.h
diff --git a/drivers/char/drm/mga_warp.c b/drivers/gpu/drm/mga/mga_warp.c
similarity index 100%
rename from drivers/char/drm/mga_warp.c
rename to drivers/gpu/drm/mga/mga_warp.c
diff --git a/drivers/gpu/drm/r128/Makefile b/drivers/gpu/drm/r128/Makefile
new file mode 100644
index 0000000..1cc72ae
--- /dev/null
+++ b/drivers/gpu/drm/r128/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+r128-y   := r128_drv.o r128_cce.o r128_state.o r128_irq.o
+
+r128-$(CONFIG_COMPAT)   += r128_ioc32.o
+
+obj-$(CONFIG_DRM_R128)	+= r128.o
diff --git a/drivers/char/drm/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c
similarity index 100%
rename from drivers/char/drm/r128_cce.c
rename to drivers/gpu/drm/r128/r128_cce.c
diff --git a/drivers/char/drm/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
similarity index 100%
rename from drivers/char/drm/r128_drv.c
rename to drivers/gpu/drm/r128/r128_drv.c
diff --git a/drivers/char/drm/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h
similarity index 100%
rename from drivers/char/drm/r128_drv.h
rename to drivers/gpu/drm/r128/r128_drv.h
diff --git a/drivers/char/drm/r128_ioc32.c b/drivers/gpu/drm/r128/r128_ioc32.c
similarity index 100%
rename from drivers/char/drm/r128_ioc32.c
rename to drivers/gpu/drm/r128/r128_ioc32.c
diff --git a/drivers/char/drm/r128_irq.c b/drivers/gpu/drm/r128/r128_irq.c
similarity index 100%
rename from drivers/char/drm/r128_irq.c
rename to drivers/gpu/drm/r128/r128_irq.c
diff --git a/drivers/char/drm/r128_state.c b/drivers/gpu/drm/r128/r128_state.c
similarity index 100%
rename from drivers/char/drm/r128_state.c
rename to drivers/gpu/drm/r128/r128_state.c
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
new file mode 100644
index 0000000..feb521e
--- /dev/null
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+radeon-y := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o
+
+radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
+
+obj-$(CONFIG_DRM_RADEON)+= radeon.o
diff --git a/drivers/char/drm/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c
similarity index 100%
rename from drivers/char/drm/r300_cmdbuf.c
rename to drivers/gpu/drm/radeon/r300_cmdbuf.c
diff --git a/drivers/char/drm/r300_reg.h b/drivers/gpu/drm/radeon/r300_reg.h
similarity index 100%
rename from drivers/char/drm/r300_reg.h
rename to drivers/gpu/drm/radeon/r300_reg.h
diff --git a/drivers/char/drm/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
similarity index 100%
rename from drivers/char/drm/radeon_cp.c
rename to drivers/gpu/drm/radeon/radeon_cp.c
diff --git a/drivers/char/drm/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
similarity index 100%
rename from drivers/char/drm/radeon_drv.c
rename to drivers/gpu/drm/radeon/radeon_drv.c
diff --git a/drivers/char/drm/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
similarity index 100%
rename from drivers/char/drm/radeon_drv.h
rename to drivers/gpu/drm/radeon/radeon_drv.h
diff --git a/drivers/char/drm/radeon_ioc32.c b/drivers/gpu/drm/radeon/radeon_ioc32.c
similarity index 100%
rename from drivers/char/drm/radeon_ioc32.c
rename to drivers/gpu/drm/radeon/radeon_ioc32.c
diff --git a/drivers/char/drm/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c
similarity index 100%
rename from drivers/char/drm/radeon_irq.c
rename to drivers/gpu/drm/radeon/radeon_irq.c
diff --git a/drivers/char/drm/radeon_mem.c b/drivers/gpu/drm/radeon/radeon_mem.c
similarity index 100%
rename from drivers/char/drm/radeon_mem.c
rename to drivers/gpu/drm/radeon/radeon_mem.c
diff --git a/drivers/char/drm/radeon_microcode.h b/drivers/gpu/drm/radeon/radeon_microcode.h
similarity index 100%
rename from drivers/char/drm/radeon_microcode.h
rename to drivers/gpu/drm/radeon/radeon_microcode.h
diff --git a/drivers/char/drm/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
similarity index 100%
rename from drivers/char/drm/radeon_state.c
rename to drivers/gpu/drm/radeon/radeon_state.c
diff --git a/drivers/gpu/drm/savage/Makefile b/drivers/gpu/drm/savage/Makefile
new file mode 100644
index 0000000..d8f84ac
--- /dev/null
+++ b/drivers/gpu/drm/savage/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y = -Iinclude/drm
+savage-y := savage_drv.o savage_bci.o savage_state.o
+
+obj-$(CONFIG_DRM_SAVAGE)+= savage.o
+
diff --git a/drivers/char/drm/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c
similarity index 100%
rename from drivers/char/drm/savage_bci.c
rename to drivers/gpu/drm/savage/savage_bci.c
diff --git a/drivers/char/drm/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
similarity index 100%
rename from drivers/char/drm/savage_drv.c
rename to drivers/gpu/drm/savage/savage_drv.c
diff --git a/drivers/char/drm/savage_drv.h b/drivers/gpu/drm/savage/savage_drv.h
similarity index 100%
rename from drivers/char/drm/savage_drv.h
rename to drivers/gpu/drm/savage/savage_drv.h
diff --git a/drivers/char/drm/savage_state.c b/drivers/gpu/drm/savage/savage_state.c
similarity index 100%
rename from drivers/char/drm/savage_state.c
rename to drivers/gpu/drm/savage/savage_state.c
diff --git a/drivers/gpu/drm/sis/Makefile b/drivers/gpu/drm/sis/Makefile
new file mode 100644
index 0000000..441c061
--- /dev/null
+++ b/drivers/gpu/drm/sis/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y = -Iinclude/drm
+sis-y := sis_drv.o sis_mm.o
+
+obj-$(CONFIG_DRM_SIS)   += sis.o
+
+
diff --git a/drivers/char/drm/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
similarity index 100%
rename from drivers/char/drm/sis_drv.c
rename to drivers/gpu/drm/sis/sis_drv.c
diff --git a/drivers/char/drm/sis_drv.h b/drivers/gpu/drm/sis/sis_drv.h
similarity index 100%
rename from drivers/char/drm/sis_drv.h
rename to drivers/gpu/drm/sis/sis_drv.h
diff --git a/drivers/char/drm/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c
similarity index 100%
rename from drivers/char/drm/sis_mm.c
rename to drivers/gpu/drm/sis/sis_mm.c
diff --git a/drivers/gpu/drm/tdfx/Makefile b/drivers/gpu/drm/tdfx/Makefile
new file mode 100644
index 0000000..0379f29
--- /dev/null
+++ b/drivers/gpu/drm/tdfx/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+tdfx-y := tdfx_drv.o
+
+obj-$(CONFIG_DRM_TDFX)	+= tdfx.o
diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
similarity index 100%
rename from drivers/char/drm/tdfx_drv.c
rename to drivers/gpu/drm/tdfx/tdfx_drv.c
diff --git a/drivers/char/drm/tdfx_drv.h b/drivers/gpu/drm/tdfx/tdfx_drv.h
similarity index 100%
rename from drivers/char/drm/tdfx_drv.h
rename to drivers/gpu/drm/tdfx/tdfx_drv.h
diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile
new file mode 100644
index 0000000..d59e258
--- /dev/null
+++ b/drivers/gpu/drm/via/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+via-y    := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o
+
+obj-$(CONFIG_DRM_VIA)	+=via.o
diff --git a/drivers/char/drm/via_3d_reg.h b/drivers/gpu/drm/via/via_3d_reg.h
similarity index 100%
rename from drivers/char/drm/via_3d_reg.h
rename to drivers/gpu/drm/via/via_3d_reg.h
diff --git a/drivers/char/drm/via_dma.c b/drivers/gpu/drm/via/via_dma.c
similarity index 100%
rename from drivers/char/drm/via_dma.c
rename to drivers/gpu/drm/via/via_dma.c
diff --git a/drivers/char/drm/via_dmablit.c b/drivers/gpu/drm/via/via_dmablit.c
similarity index 100%
rename from drivers/char/drm/via_dmablit.c
rename to drivers/gpu/drm/via/via_dmablit.c
diff --git a/drivers/char/drm/via_dmablit.h b/drivers/gpu/drm/via/via_dmablit.h
similarity index 100%
rename from drivers/char/drm/via_dmablit.h
rename to drivers/gpu/drm/via/via_dmablit.h
diff --git a/drivers/char/drm/via_drv.c b/drivers/gpu/drm/via/via_drv.c
similarity index 100%
rename from drivers/char/drm/via_drv.c
rename to drivers/gpu/drm/via/via_drv.c
diff --git a/drivers/char/drm/via_drv.h b/drivers/gpu/drm/via/via_drv.h
similarity index 100%
rename from drivers/char/drm/via_drv.h
rename to drivers/gpu/drm/via/via_drv.h
diff --git a/drivers/char/drm/via_irq.c b/drivers/gpu/drm/via/via_irq.c
similarity index 100%
rename from drivers/char/drm/via_irq.c
rename to drivers/gpu/drm/via/via_irq.c
diff --git a/drivers/char/drm/via_map.c b/drivers/gpu/drm/via/via_map.c
similarity index 100%
rename from drivers/char/drm/via_map.c
rename to drivers/gpu/drm/via/via_map.c
diff --git a/drivers/char/drm/via_mm.c b/drivers/gpu/drm/via/via_mm.c
similarity index 100%
rename from drivers/char/drm/via_mm.c
rename to drivers/gpu/drm/via/via_mm.c
diff --git a/drivers/char/drm/via_verifier.c b/drivers/gpu/drm/via/via_verifier.c
similarity index 100%
rename from drivers/char/drm/via_verifier.c
rename to drivers/gpu/drm/via/via_verifier.c
diff --git a/drivers/char/drm/via_verifier.h b/drivers/gpu/drm/via/via_verifier.h
similarity index 100%
rename from drivers/char/drm/via_verifier.h
rename to drivers/gpu/drm/via/via_verifier.h
diff --git a/drivers/char/drm/via_video.c b/drivers/gpu/drm/via/via_video.c
similarity index 100%
rename from drivers/char/drm/via_video.c
rename to drivers/gpu/drm/via/via_video.c
diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c
index 3381424..8dbf4d9 100644
--- a/drivers/ide/legacy/ide-cs.c
+++ b/drivers/ide/legacy/ide-cs.c
@@ -63,11 +63,11 @@
 
 #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
 
-#ifdef PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#ifdef CONFIG_PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, 0);
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"ide-cs.c 1.3 2002/10/26 05:45:31 (David Hinds)";
+/*static char *version =
+"ide-cs.c 1.3 2002/10/26 05:45:31 (David Hinds)";*/
 #else
 #define DEBUG(n, args...)
 #endif
@@ -375,7 +375,7 @@
 
 ======================================================================*/
 
-void ide_release(struct pcmcia_device *link)
+static void ide_release(struct pcmcia_device *link)
 {
     ide_info_t *info = link->priv;
     ide_hwif_t *hwif = info->hwif;
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 1074824..6a866d7 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -50,17 +50,19 @@
 /**
  *	linear_mergeable_bvec -- tell bio layer if two requests can be merged
  *	@q: request queue
- *	@bio: the buffer head that's been built up so far
+ *	@bvm: properties of new bio
  *	@biovec: the request that could be merged to it.
  *
  *	Return amount of bytes we can take at this offset
  */
-static int linear_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec)
+static int linear_mergeable_bvec(struct request_queue *q,
+				 struct bvec_merge_data *bvm,
+				 struct bio_vec *biovec)
 {
 	mddev_t *mddev = q->queuedata;
 	dev_info_t *dev0;
-	unsigned long maxsectors, bio_sectors = bio->bi_size >> 9;
-	sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
+	unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9;
+	sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
 
 	dev0 = which_dev(mddev, sector);
 	maxsectors = (dev0->size << 1) - (sector - (dev0->offset<<1));
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 914c04d..bcbb825 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -241,18 +241,20 @@
 /**
  *	raid0_mergeable_bvec -- tell bio layer if a two requests can be merged
  *	@q: request queue
- *	@bio: the buffer head that's been built up so far
+ *	@bvm: properties of new bio
  *	@biovec: the request that could be merged to it.
  *
  *	Return amount of bytes we can accept at this offset
  */
-static int raid0_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec)
+static int raid0_mergeable_bvec(struct request_queue *q,
+				struct bvec_merge_data *bvm,
+				struct bio_vec *biovec)
 {
 	mddev_t *mddev = q->queuedata;
-	sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
+	sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
 	int max;
 	unsigned int chunk_sectors = mddev->chunk_size >> 9;
-	unsigned int bio_sectors = bio->bi_size >> 9;
+	unsigned int bio_sectors = bvm->bi_size >> 9;
 
 	max =  (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9;
 	if (max < 0) max = 0; /* bio_add cannot handle a negative return */
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index a71277b..22bb2b1 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -439,26 +439,27 @@
 /**
  *	raid10_mergeable_bvec -- tell bio layer if a two requests can be merged
  *	@q: request queue
- *	@bio: the buffer head that's been built up so far
+ *	@bvm: properties of new bio
  *	@biovec: the request that could be merged to it.
  *
  *	Return amount of bytes we can accept at this offset
  *      If near_copies == raid_disk, there are no striping issues,
  *      but in that case, the function isn't called at all.
  */
-static int raid10_mergeable_bvec(struct request_queue *q, struct bio *bio,
-				struct bio_vec *bio_vec)
+static int raid10_mergeable_bvec(struct request_queue *q,
+				 struct bvec_merge_data *bvm,
+				 struct bio_vec *biovec)
 {
 	mddev_t *mddev = q->queuedata;
-	sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
+	sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
 	int max;
 	unsigned int chunk_sectors = mddev->chunk_size >> 9;
-	unsigned int bio_sectors = bio->bi_size >> 9;
+	unsigned int bio_sectors = bvm->bi_size >> 9;
 
 	max =  (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9;
 	if (max < 0) max = 0; /* bio_add cannot handle a negative return */
-	if (max <= bio_vec->bv_len && bio_sectors == 0)
-		return bio_vec->bv_len;
+	if (max <= biovec->bv_len && bio_sectors == 0)
+		return biovec->bv_len;
 	else
 		return max;
 }
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 3b27df5..9ce7154 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3314,15 +3314,17 @@
 /* We want read requests to align with chunks where possible,
  * but write requests don't need to.
  */
-static int raid5_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec)
+static int raid5_mergeable_bvec(struct request_queue *q,
+				struct bvec_merge_data *bvm,
+				struct bio_vec *biovec)
 {
 	mddev_t *mddev = q->queuedata;
-	sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev);
+	sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
 	int max;
 	unsigned int chunk_sectors = mddev->chunk_size >> 9;
-	unsigned int bio_sectors = bio->bi_size >> 9;
+	unsigned int bio_sectors = bvm->bi_size >> 9;
 
-	if (bio_data_dir(bio) == WRITE)
+	if ((bvm->bi_rw & 1) == WRITE)
 		return biovec->bv_len; /* always allow writes to be mergeable */
 
 	max =  (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9;
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c
index 0d5ce03..5b5a14d 100644
--- a/drivers/misc/atmel_pwm.c
+++ b/drivers/misc/atmel_pwm.c
@@ -332,7 +332,7 @@
 	p->base = ioremap(r->start, r->end - r->start + 1);
 	if (!p->base)
 		goto fail;
-	p->clk = clk_get(&pdev->dev, "mck");
+	p->clk = clk_get(&pdev->dev, "pwm_clk");
 	if (IS_ERR(p->clk)) {
 		status = PTR_ERR(p->clk);
 		p->clk = NULL;
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index 4a79b18..5c29872 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -130,10 +130,6 @@
     u_int16_t		DataUnits;
     u_int32_t		BlocksPerUnit;
     erase_unit_header_t	header;
-#if 0
-    region_info_t	region;
-    memory_handle_t	handle;
-#endif
 } partition_t;
 
 /* Partition state flags */
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index 1912d96..0cc31675 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -498,17 +498,14 @@
 	int i;
 	config_info_t t;
 	static char *probes[] = { "jedec_probe", "cfi_probe" };
-	cisinfo_t cisinfo;
 	int new_name = 0;
 
 	DEBUG(3, "link=0x%p", link);
 
 	DEBUG(2, "Validating CIS");
-	ret = pcmcia_validate_cis(link, &cisinfo);
+	ret = pcmcia_validate_cis(link, NULL);
 	if(ret != CS_SUCCESS) {
 		cs_error(link, GetTupleData, ret);
-	} else {
-		DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains);
 	}
 
 	card_settings(dev, link, &new_name);
@@ -563,9 +560,7 @@
 	DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10);
 
 	/* Get write protect status */
-	CS_CHECK(GetStatus, pcmcia_get_status(link, &status));
-	DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx",
-	      status.CardState, (unsigned long)link->win);
+	DEBUG(2, "window handle = 0x%8.8lx", (unsigned long)link->win);
 	dev->win_base = ioremap(req.Base, req.Size);
 	if(!dev->win_base) {
 		err("ioremap(%lu, %u) failed", req.Base, req.Size);
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 92dccd4..0a5745a 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -1277,8 +1277,45 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int macb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct net_device *netdev = platform_get_drvdata(pdev);
+	struct macb *bp = netdev_priv(netdev);
+
+	netif_device_detach(netdev);
+
+#ifndef CONFIG_ARCH_AT91
+	clk_disable(bp->hclk);
+#endif
+	clk_disable(bp->pclk);
+
+	return 0;
+}
+
+static int macb_resume(struct platform_device *pdev)
+{
+	struct net_device *netdev = platform_get_drvdata(pdev);
+	struct macb *bp = netdev_priv(netdev);
+
+	clk_enable(bp->pclk);
+#ifndef CONFIG_ARCH_AT91
+	clk_enable(bp->hclk);
+#endif
+
+	netif_device_attach(netdev);
+
+	return 0;
+}
+#else
+#define macb_suspend	NULL
+#define macb_resume	NULL
+#endif
+
 static struct platform_driver macb_driver = {
 	.remove		= __exit_p(macb_remove),
+	.suspend	= macb_suspend,
+	.resume		= macb_resume,
 	.driver		= {
 		.name		= "macb",
 		.owner	= THIS_MODULE,
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index d26f69b..ef671d1 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1324,7 +1324,7 @@
 		goto fail;
 	}
 
-	txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_KERNEL);
+	txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
 	if (!txs) {
 		err = -ENOMEM;
 		xenbus_dev_fatal(dev, err, "allocating tx ring page");
@@ -1340,7 +1340,7 @@
 	}
 
 	info->tx_ring_ref = err;
-	rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_KERNEL);
+	rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
 	if (!rxs) {
 		err = -ENOMEM;
 		xenbus_dev_fatal(dev, err, "allocating rx ring page");
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 1b0eb5a..e45402a 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -263,6 +263,13 @@
 	  Say Y here to support the CompactFlash controller on OMAP.
 	  Note that this doesn't support "True IDE" mode.
 
+config BFIN_CFPCMCIA
+	tristate "Blackfin CompactFlash PCMCIA Driver"
+	depends on PCMCIA && BLACKFIN
+	help
+	  Say Y here to support the CompactFlash PCMCIA driver for Blackfin.
+
+
 config AT91_CF
 	tristate "AT91 CompactFlash Controller"
 	depends on PCMCIA && ARCH_AT91RM9200
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 6f6478b..85c6cc9 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -36,6 +36,7 @@
 obj-$(CONFIG_PCMCIA_VRC4171)			+= vrc4171_card.o
 obj-$(CONFIG_PCMCIA_VRC4173)			+= vrc4173_cardu.o
 obj-$(CONFIG_OMAP_CF)				+= omap_cf.o
+obj-$(CONFIG_BFIN_CFPCMCIA)			+= bfin_cf_pcmcia.o
 obj-$(CONFIG_AT91_CF)				+= at91_cf.o
 obj-$(CONFIG_ELECTRA_CF)			+= electra_cf.o
 
diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h
index 1e467bb..a53ef59 100644
--- a/drivers/pcmcia/au1000_generic.h
+++ b/drivers/pcmcia/au1000_generic.h
@@ -26,7 +26,6 @@
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"
 
@@ -34,9 +33,9 @@
 #define AU1000_PCMCIA_IO_SPEED       (255)
 #define AU1000_PCMCIA_MEM_SPEED      (300)
 
-#define AU1X_SOCK0_IO        0xF00000000
-#define AU1X_SOCK0_PHYS_ATTR 0xF40000000
-#define AU1X_SOCK0_PHYS_MEM  0xF80000000
+#define AU1X_SOCK0_IO        0xF00000000ULL
+#define AU1X_SOCK0_PHYS_ATTR 0xF40000000ULL
+#define AU1X_SOCK0_PHYS_MEM  0xF80000000ULL
 /* pseudo 32 bit phys addresses, which get fixed up to the
  * real 36 bit address in fixup_bigphys_addr() */
 #define AU1X_SOCK0_PSEUDO_PHYS_ATTR 0xF4000000
@@ -45,16 +44,20 @@
 /* pcmcia socket 1 needs external glue logic so the memory map
  * differs from board to board.
  */
-#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_PB1200)
-#define AU1X_SOCK1_IO        0xF08000000
-#define AU1X_SOCK1_PHYS_ATTR 0xF48000000
-#define AU1X_SOCK1_PHYS_MEM  0xF88000000
+#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || \
+    defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550) || \
+    defined(CONFIG_MIPS_PB1200)
+#define AU1X_SOCK1_IO        0xF08000000ULL
+#define AU1X_SOCK1_PHYS_ATTR 0xF48000000ULL
+#define AU1X_SOCK1_PHYS_MEM  0xF88000000ULL
 #define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000
 #define AU1X_SOCK1_PSEUDO_PHYS_MEM  0xF8800000
-#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_DB1200)
-#define AU1X_SOCK1_IO        0xF04000000
-#define AU1X_SOCK1_PHYS_ATTR 0xF44000000
-#define AU1X_SOCK1_PHYS_MEM  0xF84000000
+#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || \
+      defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550) || \
+      defined(CONFIG_MIPS_DB1200)
+#define AU1X_SOCK1_IO        0xF04000000ULL
+#define AU1X_SOCK1_PHYS_ATTR 0xF44000000ULL
+#define AU1X_SOCK1_PHYS_MEM  0xF84000000ULL
 #define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4400000
 #define AU1X_SOCK1_PSEUDO_PHYS_MEM  0xF8400000
 #endif
diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c
index 157e414..aa1cd4d 100644
--- a/drivers/pcmcia/au1000_pb1x00.c
+++ b/drivers/pcmcia/au1000_pb1x00.c
@@ -35,7 +35,6 @@
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/bus_ops.h>
 #include "cs_internal.h"
diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c
index c78ed53..8a9b18c 100644
--- a/drivers/pcmcia/au1000_xxs1500.c
+++ b/drivers/pcmcia/au1000_xxs1500.c
@@ -39,7 +39,6 @@
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/bus_ops.h>
 #include "cs_internal.h"
diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c
new file mode 100644
index 0000000..bb73388
--- /dev/null
+++ b/drivers/pcmcia/bfin_cf_pcmcia.c
@@ -0,0 +1,339 @@
+/*
+ * file: drivers/pcmcia/bfin_cf.c
+ *
+ * based on: drivers/pcmcia/omap_cf.c
+ * omap_cf.c -- OMAP 16xx CompactFlash controller driver
+ *
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006-2008 Michael Hennerich Analog Devices Inc.
+ *
+ * bugs:         enter bugs at http://blackfin.uclinux.org/
+ *
+ * this program is free software; you can redistribute it and/or modify
+ * it under the terms of the gnu general public license as published by
+ * the free software foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * this program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * merchantability or fitness for a particular purpose.  see the
+ * gnu general public license for more details.
+ *
+ * you should have received a copy of the gnu general public license
+ * along with this program; see the file copying.
+ * if not, write to the free software foundation,
+ * 59 temple place - suite 330, boston, ma 02111-1307, usa.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <pcmcia/ss.h>
+#include <pcmcia/cisreg.h>
+#include <asm/gpio.h>
+
+#define	SZ_1K	0x00000400
+#define	SZ_8K	0x00002000
+#define	SZ_2K	(2 * SZ_1K)
+
+#define	POLL_INTERVAL	(2 * HZ)
+
+#define	CF_ATASEL_ENA 	0x20311802	/* Inverts RESET */
+#define	CF_ATASEL_DIS 	0x20311800
+
+#define bfin_cf_present(pfx) (gpio_get_value(pfx))
+
+/*--------------------------------------------------------------------------*/
+
+static const char driver_name[] = "bfin_cf_pcmcia";
+
+struct bfin_cf_socket {
+	struct pcmcia_socket socket;
+
+	struct timer_list timer;
+	unsigned present:1;
+	unsigned active:1;
+
+	struct platform_device *pdev;
+	unsigned long phys_cf_io;
+	unsigned long phys_cf_attr;
+	u_int irq;
+	u_short cd_pfx;
+};
+
+/*--------------------------------------------------------------------------*/
+static int bfin_cf_reset(void)
+{
+	outw(0, CF_ATASEL_ENA);
+	mdelay(200);
+	outw(0, CF_ATASEL_DIS);
+
+	return 0;
+}
+
+static int bfin_cf_ss_init(struct pcmcia_socket *s)
+{
+	return 0;
+}
+
+/* the timer is primarily to kick this socket's pccardd */
+static void bfin_cf_timer(unsigned long _cf)
+{
+	struct bfin_cf_socket *cf = (void *)_cf;
+	unsigned short present = bfin_cf_present(cf->cd_pfx);
+
+	if (present != cf->present) {
+		cf->present = present;
+		dev_dbg(&cf->pdev->dev, ": card %s\n",
+			 present ? "present" : "gone");
+		pcmcia_parse_events(&cf->socket, SS_DETECT);
+	}
+
+	if (cf->active)
+		mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
+}
+
+static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
+{
+	struct bfin_cf_socket *cf;
+
+	if (!sp)
+		return -EINVAL;
+
+	cf = container_of(s, struct bfin_cf_socket, socket);
+
+	if (bfin_cf_present(cf->cd_pfx)) {
+		*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
+		s->irq.AssignedIRQ = 0;
+		s->pci_irq = cf->irq;
+
+	} else
+		*sp = 0;
+	return 0;
+}
+
+static int
+bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
+{
+
+	struct bfin_cf_socket *cf;
+	cf = container_of(sock, struct bfin_cf_socket, socket);
+
+	switch (s->Vcc) {
+	case 0:
+	case 33:
+		break;
+	case 50:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (s->flags & SS_RESET) {
+		disable_irq(cf->irq);
+		bfin_cf_reset();
+		enable_irq(cf->irq);
+	}
+
+	dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n",
+		 s->Vcc, s->io_irq, s->flags, s->csc_mask);
+
+	return 0;
+}
+
+static int bfin_cf_ss_suspend(struct pcmcia_socket *s)
+{
+	return bfin_cf_set_socket(s, &dead_socket);
+}
+
+/* regions are 2K each:  mem, attrib, io (and reserved-for-ide) */
+
+static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+	struct bfin_cf_socket *cf;
+
+	cf = container_of(s, struct bfin_cf_socket, socket);
+	io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
+	io->start = cf->phys_cf_io;
+	io->stop = io->start + SZ_2K - 1;
+	return 0;
+}
+
+static int
+bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
+{
+	struct bfin_cf_socket *cf;
+
+	if (map->card_start)
+		return -EINVAL;
+	cf = container_of(s, struct bfin_cf_socket, socket);
+	map->static_start = cf->phys_cf_io;
+	map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
+	if (map->flags & MAP_ATTRIB)
+		map->static_start = cf->phys_cf_attr;
+
+	return 0;
+}
+
+static struct pccard_operations bfin_cf_ops = {
+	.init = bfin_cf_ss_init,
+	.suspend = bfin_cf_ss_suspend,
+	.get_status = bfin_cf_get_status,
+	.set_socket = bfin_cf_set_socket,
+	.set_io_map = bfin_cf_set_io_map,
+	.set_mem_map = bfin_cf_set_mem_map,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __devinit bfin_cf_probe(struct platform_device *pdev)
+{
+	struct bfin_cf_socket *cf;
+	struct resource *io_mem, *attr_mem;
+	int irq;
+	unsigned short cd_pfx;
+	int status = 0;
+
+	dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq)
+		return -EINVAL;
+
+	cd_pfx = platform_get_irq(pdev, 1);	/*Card Detect GPIO PIN */
+
+	if (gpio_request(cd_pfx, "pcmcia: CD")) {
+		dev_err(&pdev->dev,
+		       "Failed ro request Card Detect GPIO_%d\n",
+		       cd_pfx);
+		return -EBUSY;
+	}
+	gpio_direction_input(cd_pfx);
+
+	cf = kzalloc(sizeof *cf, GFP_KERNEL);
+	if (!cf) {
+		gpio_free(cd_pfx);
+		return -ENOMEM;
+	}
+
+	cf->cd_pfx = cd_pfx;
+
+	setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf);
+
+	cf->pdev = pdev;
+	platform_set_drvdata(pdev, cf);
+
+	cf->irq = irq;
+	cf->socket.pci_irq = irq;
+
+	set_irq_type(irq, IRQF_TRIGGER_LOW);
+
+	io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+	if (!io_mem || !attr_mem)
+		goto fail0;
+
+	cf->phys_cf_io = io_mem->start;
+	cf->phys_cf_attr = attr_mem->start;
+
+	/* pcmcia layer only remaps "real" memory */
+	cf->socket.io_offset = (unsigned long)
+	    ioremap(cf->phys_cf_io, SZ_2K);
+
+	if (!cf->socket.io_offset)
+		goto fail0;
+
+	dev_err(&pdev->dev, ": on irq %d\n", irq);
+
+	dev_dbg(&pdev->dev, ": %s\n",
+		 bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)");
+
+	cf->socket.owner = THIS_MODULE;
+	cf->socket.dev.parent = &pdev->dev;
+	cf->socket.ops = &bfin_cf_ops;
+	cf->socket.resource_ops = &pccard_static_ops;
+	cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
+	    | SS_CAP_MEM_ALIGN;
+	cf->socket.map_size = SZ_2K;
+
+	status = pcmcia_register_socket(&cf->socket);
+	if (status < 0)
+		goto fail2;
+
+	cf->active = 1;
+	mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
+	return 0;
+
+fail2:
+	iounmap((void __iomem *)cf->socket.io_offset);
+	release_mem_region(cf->phys_cf_io, SZ_8K);
+
+fail0:
+	gpio_free(cf->cd_pfx);
+	kfree(cf);
+	platform_set_drvdata(pdev, NULL);
+
+	return status;
+}
+
+static int __devexit bfin_cf_remove(struct platform_device *pdev)
+{
+	struct bfin_cf_socket *cf = platform_get_drvdata(pdev);
+
+	gpio_free(cf->cd_pfx);
+	cf->active = 0;
+	pcmcia_unregister_socket(&cf->socket);
+	del_timer_sync(&cf->timer);
+	iounmap((void __iomem *)cf->socket.io_offset);
+	release_mem_region(cf->phys_cf_io, SZ_8K);
+	platform_set_drvdata(pdev, NULL);
+	kfree(cf);
+	return 0;
+}
+
+static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+	return pcmcia_socket_dev_suspend(&pdev->dev, mesg);
+}
+
+static int bfin_cf_resume(struct platform_device *pdev)
+{
+	return pcmcia_socket_dev_resume(&pdev->dev);
+}
+
+static struct platform_driver bfin_cf_driver = {
+	.driver = {
+		   .name = (char *)driver_name,
+		   .owner = THIS_MODULE,
+		   },
+	.probe = bfin_cf_probe,
+	.remove = __devexit_p(bfin_cf_remove),
+	.suspend = bfin_cf_suspend,
+	.resume = bfin_cf_resume,
+};
+
+static int __init bfin_cf_init(void)
+{
+	return platform_driver_register(&bfin_cf_driver);
+}
+
+static void __exit bfin_cf_exit(void)
+{
+	platform_driver_unregister(&bfin_cf_driver);
+}
+
+module_init(bfin_cf_init);
+module_exit(bfin_cf_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>")
+MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index fb2f38d..911ca0e 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -30,11 +30,9 @@
 #include <asm/irq.h>
 #include <asm/io.h>
 
-#define IN_CARD_SERVICES
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
 #include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"
 
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 3637953..9fcff0c 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -30,7 +30,6 @@
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
 #include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"
@@ -1439,10 +1438,11 @@
     
 ======================================================================*/
 
-int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_t *info)
+int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, unsigned int *info)
 {
     tuple_t *tuple;
     cisparse_t *p;
+    unsigned int count = 0;
     int ret, reserved, dev_ok = 0, ident_ok = 0;
 
     if (!s)
@@ -1457,7 +1457,7 @@
 	return CS_OUT_OF_RESOURCE;
     }
 
-    info->Chains = reserved = 0;
+    count = reserved = 0;
     tuple->DesiredTuple = RETURN_FIRST_TUPLE;
     tuple->Attributes = TUPLE_RETURN_COMMON;
     ret = pccard_get_first_tuple(s, function, tuple);
@@ -1482,7 +1482,7 @@
     if (!dev_ok && !ident_ok)
 	goto done;
 
-    for (info->Chains = 1; info->Chains < MAX_TUPLES; info->Chains++) {
+    for (count = 1; count < MAX_TUPLES; count++) {
 	ret = pccard_get_next_tuple(s, function, tuple);
 	if (ret != CS_SUCCESS) break;
 	if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
@@ -1490,11 +1490,13 @@
 	    ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
 	    reserved++;
     }
-    if ((info->Chains == MAX_TUPLES) || (reserved > 5) ||
-	((!dev_ok || !ident_ok) && (info->Chains > 10)))
-	info->Chains = 0;
+    if ((count) || (reserved > 5) ||
+	((!dev_ok || !ident_ok) && (count > 10)))
+	count = 0;
 
 done:
+    if (info)
+	    *info = count;
     kfree(tuple);
     kfree(p);
     return CS_SUCCESS;
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 29276bd..d120739 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -32,11 +32,9 @@
 #include <asm/system.h>
 #include <asm/irq.h>
 
-#define IN_CARD_SERVICES
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
 #include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
@@ -238,7 +236,6 @@
 
 	init_completion(&socket->socket_released);
 	init_completion(&socket->thread_done);
-	init_waitqueue_head(&socket->thread_wait);
 	mutex_init(&socket->skt_mutex);
 	spin_lock_init(&socket->thread_lock);
 
@@ -278,10 +275,9 @@
 
 	cs_dbg(socket, 0, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
 
-	if (socket->thread) {
-		wake_up(&socket->thread_wait);
+	if (socket->thread)
 		kthread_stop(socket->thread);
-	}
+
 	release_cis_mem(socket);
 
 	/* remove from our own list */
@@ -635,7 +631,6 @@
 static int pccardd(void *__skt)
 {
 	struct pcmcia_socket *skt = __skt;
-	DECLARE_WAITQUEUE(wait, current);
 	int ret;
 
 	skt->thread = current;
@@ -656,7 +651,6 @@
 	if (ret)
 		dev_warn(&skt->dev, "err %d adding socket attributes\n", ret);
 
-	add_wait_queue(&skt->thread_wait, &wait);
 	complete(&skt->thread_done);
 
 	set_freezable();
@@ -694,8 +688,6 @@
 	/* make sure we are running before we exit */
 	set_current_state(TASK_RUNNING);
 
-	remove_wait_queue(&skt->thread_wait, &wait);
-
 	/* remove from the device core */
 	pccard_sysfs_remove_socket(&skt->dev);
 	device_unregister(&skt->dev);
@@ -716,7 +708,7 @@
 		s->thread_events |= events;
 		spin_unlock_irqrestore(&s->thread_lock, flags);
 
-		wake_up(&s->thread_wait);
+		wake_up_process(s->thread);
 	}
 } /* pcmcia_parse_events */
 EXPORT_SYMBOL(pcmcia_parse_events);
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index e7d5d14..63dc1a2 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -26,18 +26,6 @@
 #define CLIENT_WIN_REQ(i)	(0x1<<(i))
 #define CLIENT_CARDBUS		0x8000
 
-#define REGION_MAGIC	0xE3C9
-typedef struct region_t {
-    u_short		region_magic;
-    u_short		state;
-    dev_info_t		dev_info;
-    struct pcmcia_device	*mtd;
-    u_int		MediaID;
-    region_info_t	info;
-} region_t;
-
-#define REGION_STALE	0x01
-
 /* Each card function gets one of these guys */
 typedef struct config_t {
 	struct kref	ref;
@@ -130,7 +118,6 @@
 int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req);
 int pccard_get_configuration_info(struct pcmcia_socket *s, struct pcmcia_device *p_dev, config_info_t *config);
 int pccard_reset_card(struct pcmcia_socket *skt);
-int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, cs_status_t *status);
 
 
 struct pcmcia_callback{
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index e407754..4174d96 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -25,7 +25,6 @@
 #include <linux/kref.h>
 #include <linux/dma-mapping.h>
 
-#define IN_CARD_SERVICES
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/cistpl.h>
@@ -741,9 +740,8 @@
 
 static int pcmcia_card_add(struct pcmcia_socket *s)
 {
-	cisinfo_t cisinfo;
 	cistpl_longlink_mfc_t mfc;
-	unsigned int no_funcs, i;
+	unsigned int no_funcs, i, no_chains;
 	int ret = 0;
 
 	if (!(s->resource_setup_done)) {
@@ -757,8 +755,8 @@
 		return -EAGAIN; /* try again, but later... */
 	}
 
-	ret = pccard_validate_cis(s, BIND_FN_ALL, &cisinfo);
-	if (ret || !cisinfo.Chains) {
+	ret = pccard_validate_cis(s, BIND_FN_ALL, &no_chains);
+	if (ret || !no_chains) {
 		ds_dbg(0, "invalid CIS or invalid resources\n");
 		return -ENODEV;
 	}
@@ -852,7 +850,7 @@
 {
 	struct pcmcia_socket *s = dev->socket;
 	const struct firmware *fw;
-	char path[20];
+	char path[FIRMWARE_NAME_MAX];
 	int ret = -ENOMEM;
 	int no_funcs;
 	int old_funcs;
@@ -864,7 +862,7 @@
 
 	ds_dbg(1, "trying to load CIS file %s\n", filename);
 
-	if (strlen(filename) > 14) {
+	if (strlen(filename) > (FIRMWARE_NAME_MAX - 1)) {
 		printk(KERN_WARNING "pcmcia: CIS filename is too long [%s]\n",
 			filename);
 		return -EINVAL;
diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c
index f2e810f..fb2bc1f 100644
--- a/drivers/pcmcia/hd64465_ss.c
+++ b/drivers/pcmcia/hd64465_ss.c
@@ -1,6 +1,4 @@
 /*
- * $Id: hd64465_ss.c,v 1.7 2003/07/06 14:42:50 lethal Exp $
- *
  * Device driver for the PCMCIA controller module of the
  * Hitachi HD64465 handheld companion chip.
  *
@@ -48,7 +46,6 @@
 #include <pcmcia/cistpl.h>
 #include <pcmcia/ds.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/bulkmem.h>
 #include "cs_internal.h"
 
 #define MODNAME "hd64465_ss"
diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c
index e136186..46561fa 100644
--- a/drivers/pcmcia/i82092.c
+++ b/drivers/pcmcia/i82092.c
@@ -5,8 +5,6 @@
  *
  * Author: Arjan Van De Ven <arjanv@redhat.com>
  * Loosly based on i82365.c from the pcmcia-cs package
- *
- * $Id: i82092aa.c,v 1.2 2001/10/23 14:43:34 arjanv Exp $
  */
 
 #include <linux/kernel.h>
diff --git a/drivers/pcmcia/i82092aa.h b/drivers/pcmcia/i82092aa.h
index b0d4533..8836d39 100644
--- a/drivers/pcmcia/i82092aa.h
+++ b/drivers/pcmcia/i82092aa.h
@@ -3,8 +3,6 @@
 
 #include <linux/interrupt.h>
 
-/* $Id: i82092aa.h,v 1.1.1.1 2001/09/19 14:53:15 dwmw2 Exp $ */
-
 /* Debuging defines */
 #ifdef NOTRACE
 #define enter(x)   printk("Enter: %s, %s line %i\n",x,__FILE__,__LINE__)
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index 32a2ab1..68f6b27 100644
--- a/drivers/pcmcia/i82365.c
+++ b/drivers/pcmcia/i82365.c
@@ -1263,7 +1263,7 @@
 
     ret = driver_register(&i82365_driver);
     if (ret)
-	return ret;
+	goto err_out;
 
     i82365_device = platform_device_alloc("i82365", 0);
     if (i82365_device) {
@@ -1273,10 +1273,8 @@
     } else
 	    ret = -ENOMEM;
 
-    if (ret) {
-	driver_unregister(&i82365_driver);
-	return ret;
-    }
+    if (ret)
+	goto err_driver_unregister;
 
     printk(KERN_INFO "Intel ISA PCIC probe: ");
     sockets = 0;
@@ -1285,16 +1283,17 @@
 
     if (sockets == 0) {
 	printk("not found.\n");
-	platform_device_unregister(i82365_device);
-	release_region(i365_base, 2);
-	driver_unregister(&i82365_driver);
-	return -ENODEV;
+	ret = -ENODEV;
+	goto err_dev_unregister;
     }
 
     /* Set up interrupt handler(s) */
     if (grab_irq != 0)
-	request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
-    
+	ret = request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
+
+    if (ret)
+	goto err_socket_release;
+
     /* register sockets with the pcmcia core */
     for (i = 0; i < sockets; i++) {
 	    socket[i].socket.dev.parent = &i82365_device->dev;
@@ -1324,7 +1323,23 @@
     }
     
     return 0;
-    
+err_socket_release:
+    for (i = 0; i < sockets; i++) {
+	/* Turn off all interrupt sources! */
+	i365_set(i, I365_CSCINT, 0);
+	release_region(socket[i].ioaddr, 2);
+    }
+err_dev_unregister:
+    platform_device_unregister(i82365_device);
+    release_region(i365_base, 2);
+#ifdef CONFIG_PNP
+    if (i82365_pnpdev)
+	pnp_disable_dev(i82365_pnpdev);
+#endif
+err_driver_unregister:
+    driver_unregister(&i82365_driver);
+err_out:
+    return ret;
 } /* init_i82365 */
 
 static void __exit exit_i82365(void)
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index ac70d2c..13a5fbd 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -1,7 +1,7 @@
 /*
  * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series.
  *
- * (C) 1999-2000 Magnus Damm <damm@bitsmart.com>
+ * (C) 1999-2000 Magnus Damm <damm@opensource.se>
  * (C) 2001-2002 Montavista Software, Inc.
  *     <mlocke@mvista.com>
  *
@@ -60,7 +60,6 @@
 #include <asm/of_device.h>
 #include <asm/of_platform.h>
 
-#include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/ss.h>
diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c
index 5f186ab..afd00e7 100644
--- a/drivers/pcmcia/pcmcia_ioctl.c
+++ b/drivers/pcmcia/pcmcia_ioctl.c
@@ -29,10 +29,10 @@
 #include <linux/pci.h>
 #include <linux/workqueue.h>
 
-#define IN_CARD_SERVICES
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
 #include <pcmcia/ss.h>
 
@@ -138,6 +138,154 @@
 }
 #endif
 
+
+#ifdef CONFIG_PCMCIA_PROBE
+
+static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
+{
+	int irq;
+	u32 mask;
+
+	irq = adj->resource.irq.IRQ;
+	if ((irq < 0) || (irq > 15))
+		return CS_BAD_IRQ;
+
+	if (adj->Action != REMOVE_MANAGED_RESOURCE)
+		return 0;
+
+	mask = 1 << irq;
+
+	if (!(s->irq_mask & mask))
+		return 0;
+
+	s->irq_mask &= ~mask;
+
+	return 0;
+}
+
+#else
+
+static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
+	return CS_SUCCESS;
+}
+
+#endif
+
+static int pcmcia_adjust_resource_info(adjust_t *adj)
+{
+	struct pcmcia_socket *s;
+	int ret = CS_UNSUPPORTED_FUNCTION;
+	unsigned long flags;
+
+	down_read(&pcmcia_socket_list_rwsem);
+	list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
+
+		if (adj->Resource == RES_IRQ)
+			ret = adjust_irq(s, adj);
+
+		else if (s->resource_ops->add_io) {
+			unsigned long begin, end;
+
+			/* you can't use the old interface if the new
+			 * one was used before */
+			spin_lock_irqsave(&s->lock, flags);
+			if ((s->resource_setup_new) &&
+			    !(s->resource_setup_old)) {
+				spin_unlock_irqrestore(&s->lock, flags);
+				continue;
+			} else if (!(s->resource_setup_old))
+				s->resource_setup_old = 1;
+			spin_unlock_irqrestore(&s->lock, flags);
+
+			switch (adj->Resource) {
+			case RES_MEMORY_RANGE:
+				begin = adj->resource.memory.Base;
+				end = adj->resource.memory.Base + adj->resource.memory.Size - 1;
+				if (s->resource_ops->add_mem)
+					ret =s->resource_ops->add_mem(s, adj->Action, begin, end);
+			case RES_IO_RANGE:
+				begin = adj->resource.io.BasePort;
+				end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1;
+				if (s->resource_ops->add_io)
+					ret = s->resource_ops->add_io(s, adj->Action, begin, end);
+			}
+			if (!ret) {
+				/* as there's no way we know this is the
+				 * last call to adjust_resource_info, we
+				 * always need to assume this is the latest
+				 * one... */
+				spin_lock_irqsave(&s->lock, flags);
+				s->resource_setup_done = 1;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+	}
+	up_read(&pcmcia_socket_list_rwsem);
+
+	return (ret);
+}
+
+/** pccard_get_status
+ *
+ * Get the current socket state bits.  We don't support the latched
+ * SocketState yet: I haven't seen any point for it.
+ */
+
+static int pccard_get_status(struct pcmcia_socket *s,
+			     struct pcmcia_device *p_dev,
+			     cs_status_t *status)
+{
+	config_t *c;
+	int val;
+
+	s->ops->get_status(s, &val);
+	status->CardState = status->SocketState = 0;
+	status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
+	status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
+	status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
+	status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
+	if (s->state & SOCKET_SUSPEND)
+		status->CardState |= CS_EVENT_PM_SUSPEND;
+	if (!(s->state & SOCKET_PRESENT))
+		return CS_NO_CARD;
+
+	c = (p_dev) ? p_dev->function_config : NULL;
+
+	if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
+	    (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
+		u_char reg;
+		if (c->CardValues & PRESENT_PIN_REPLACE) {
+			pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
+			status->CardState |=
+				(reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
+			status->CardState |=
+				(reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
+			status->CardState |=
+				(reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
+			status->CardState |=
+				(reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
+		} else {
+			/* No PRR?  Then assume we're always ready */
+			status->CardState |= CS_EVENT_READY_CHANGE;
+		}
+		if (c->CardValues & PRESENT_EXT_STATUS) {
+			pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
+			status->CardState |=
+				(reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
+		}
+		return CS_SUCCESS;
+	}
+	status->CardState |=
+		(val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
+	status->CardState |=
+		(val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
+	status->CardState |=
+		(val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
+	status->CardState |=
+		(val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
+	return CS_SUCCESS;
+} /* pccard_get_status */
+
 /*======================================================================
 
     These manage a ring buffer of events pending for one user process
@@ -546,8 +694,6 @@
 
 /*====================================================================*/
 
-extern int pcmcia_adjust_resource_info(adjust_t *adj);
-
 static int ds_ioctl(struct inode * inode, struct file * file,
 		    u_int cmd, u_long arg)
 {
@@ -649,7 +795,7 @@
 	mutex_lock(&s->skt_mutex);
 	pcmcia_validate_mem(s);
 	mutex_unlock(&s->skt_mutex);
-	ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo);
+	ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo.Chains);
 	break;
     case DS_SUSPEND_CARD:
 	ret = pcmcia_suspend_card(s);
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index 1d128fb..4884a18 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -21,11 +21,9 @@
 #include <linux/pci.h>
 #include <linux/device.h>
 
-#define IN_CARD_SERVICES
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
 #include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
@@ -311,74 +309,6 @@
 EXPORT_SYMBOL(pcmcia_get_window);
 
 
-/** pccard_get_status
- *
- * Get the current socket state bits.  We don't support the latched
- * SocketState yet: I haven't seen any point for it.
- */
-
-int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev,
-		      cs_status_t *status)
-{
-	config_t *c;
-	int val;
-
-	s->ops->get_status(s, &val);
-	status->CardState = status->SocketState = 0;
-	status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
-	status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
-	status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
-	status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
-	if (s->state & SOCKET_SUSPEND)
-		status->CardState |= CS_EVENT_PM_SUSPEND;
-	if (!(s->state & SOCKET_PRESENT))
-		return CS_NO_CARD;
-
-	c = (p_dev) ? p_dev->function_config : NULL;
-
-	if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
-	    (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
-		u_char reg;
-		if (c->CardValues & PRESENT_PIN_REPLACE) {
-			pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
-			status->CardState |=
-				(reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
-			status->CardState |=
-				(reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
-			status->CardState |=
-				(reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
-			status->CardState |=
-				(reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
-		} else {
-			/* No PRR?  Then assume we're always ready */
-			status->CardState |= CS_EVENT_READY_CHANGE;
-		}
-		if (c->CardValues & PRESENT_EXT_STATUS) {
-			pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
-			status->CardState |=
-				(reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
-		}
-		return CS_SUCCESS;
-	}
-	status->CardState |=
-		(val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
-	status->CardState |=
-		(val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
-	status->CardState |=
-		(val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
-	status->CardState |=
-		(val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
-	return CS_SUCCESS;
-} /* pccard_get_status */
-
-int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status)
-{
-	return pccard_get_status(p_dev->socket, p_dev, status);
-}
-EXPORT_SYMBOL(pcmcia_get_status);
-
-
-
 /** pcmcia_get_mem_page
  *
  * Change the card address of an already open memory window.
@@ -812,6 +742,15 @@
 		type = IRQF_SHARED;
 
 #ifdef CONFIG_PCMCIA_PROBE
+
+#ifdef IRQ_NOAUTOEN
+	/* if the underlying IRQ infrastructure allows for it, only allocate
+	 * the IRQ, but do not enable it
+	 */
+	if (!(req->Attributes & IRQ_HANDLE_PRESENT))
+		type |= IRQ_NOAUTOEN;
+#endif /* IRQ_NOAUTOEN */
+
 	if (s->irq.AssignedIRQ != 0) {
 		/* If the interrupt is already assigned, it must be the same */
 		irq = s->irq.AssignedIRQ;
@@ -966,7 +905,7 @@
 	pcmcia_release_configuration(p_dev);
 	pcmcia_release_io(p_dev, &p_dev->io);
 	pcmcia_release_irq(p_dev, &p_dev->irq);
-	if (&p_dev->win)
+	if (p_dev->win)
 		pcmcia_release_window(p_dev->win);
 }
 EXPORT_SYMBOL(pcmcia_disable_device);
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 9414163..ccfdf19 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -33,7 +33,6 @@
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 
 #include "cs_internal.h"
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
index ce22262..c0e2afc 100644
--- a/drivers/pcmcia/rsrc_mgr.c
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -21,86 +21,6 @@
 #include "cs_internal.h"
 
 
-#ifdef CONFIG_PCMCIA_IOCTL
-
-#ifdef CONFIG_PCMCIA_PROBE
-
-static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
-{
-	int irq;
-	u32 mask;
-
-	irq = adj->resource.irq.IRQ;
-	if ((irq < 0) || (irq > 15))
-		return CS_BAD_IRQ;
-
-	if (adj->Action != REMOVE_MANAGED_RESOURCE)
-		return 0;
-
-	mask = 1 << irq;
-
-	if (!(s->irq_mask & mask))
-		return 0;
-
-	s->irq_mask &= ~mask;
-
-	return 0;
-}
-
-#else
-
-static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
-	return CS_SUCCESS;
-}
-
-#endif
-
-
-int pcmcia_adjust_resource_info(adjust_t *adj)
-{
-	struct pcmcia_socket *s;
-	int ret = CS_UNSUPPORTED_FUNCTION;
-	unsigned long flags;
-
-	down_read(&pcmcia_socket_list_rwsem);
-	list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
-
-		if (adj->Resource == RES_IRQ)
-			ret = adjust_irq(s, adj);
-
-		else if (s->resource_ops->adjust_resource) {
-
-			/* you can't use the old interface if the new
-			 * one was used before */
-			spin_lock_irqsave(&s->lock, flags);
-			if ((s->resource_setup_new) &&
-			    !(s->resource_setup_old)) {
-				spin_unlock_irqrestore(&s->lock, flags);
-				continue;
-			} else if (!(s->resource_setup_old))
-				s->resource_setup_old = 1;
-			spin_unlock_irqrestore(&s->lock, flags);
-
-			ret = s->resource_ops->adjust_resource(s, adj);
-			if (!ret) {
-				/* as there's no way we know this is the
-				 * last call to adjust_resource_info, we
-				 * always need to assume this is the latest
-				 * one... */
-				spin_lock_irqsave(&s->lock, flags);
-				s->resource_setup_done = 1;
-				spin_unlock_irqrestore(&s->lock, flags);
-			}
-		}
-	}
-	up_read(&pcmcia_socket_list_rwsem);
-
-	return (ret);
-}
-EXPORT_SYMBOL(pcmcia_adjust_resource_info);
-
-#endif
-
 int pcmcia_validate_mem(struct pcmcia_socket *s)
 {
 	if (s->resource_ops->validate_mem)
@@ -164,7 +84,8 @@
 	.adjust_io_region = NULL,
 	.find_io = NULL,
 	.find_mem = NULL,
-	.adjust_resource = NULL,
+	.add_io = NULL,
+	.add_mem = NULL,
 	.init = static_init,
 	.exit = NULL,
 };
@@ -264,7 +185,8 @@
 	.adjust_io_region = iodyn_adjust_io_region,
 	.find_io = iodyn_find_io_region,
 	.find_mem = NULL,
-	.adjust_resource = NULL,
+	.add_io = NULL,
+	.add_mem = NULL,
 	.init = static_init,
 	.exit = NULL,
 };
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 0fcf763..d0c1d63 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -31,7 +31,6 @@
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
 #include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"
 
@@ -261,21 +260,22 @@
 ======================================================================*/
 
 /* Validation function for cards with a valid CIS */
-static int readable(struct pcmcia_socket *s, struct resource *res, cisinfo_t *info)
+static int readable(struct pcmcia_socket *s, struct resource *res,
+		    unsigned int *count)
 {
 	int ret = -1;
 
 	s->cis_mem.res = res;
 	s->cis_virt = ioremap(res->start, s->map_size);
 	if (s->cis_virt) {
-		ret = pccard_validate_cis(s, BIND_FN_ALL, info);
+		ret = pccard_validate_cis(s, BIND_FN_ALL, count);
 		/* invalidate mapping and CIS cache */
 		iounmap(s->cis_virt);
 		s->cis_virt = NULL;
 		destroy_cis_cache(s);
 	}
 	s->cis_mem.res = NULL;
-	if ((ret != 0) || (info->Chains == 0))
+	if ((ret != 0) || (count == 0))
 		return 0;
 	return 1;
 }
@@ -316,7 +316,7 @@
 cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
 {
 	struct resource *res1, *res2;
-	cisinfo_t info1, info2;
+	unsigned int info1, info2;
 	int ret = 0;
 
 	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
@@ -330,7 +330,7 @@
 	free_region(res2);
 	free_region(res1);
 
-	return (ret == 2) && (info1.Chains == info2.Chains);
+	return (ret == 2) && (info1 == info2);
 }
 
 static int
@@ -766,21 +766,6 @@
 }
 
 
-static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj)
-{
-	unsigned long end;
-
-	switch (adj->Resource) {
-	case RES_MEMORY_RANGE:
-		end = adj->resource.memory.Base + adj->resource.memory.Size - 1;
-		return adjust_memory(s, adj->Action, adj->resource.memory.Base, end);
-	case RES_IO_RANGE:
-		end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1;
-		return adjust_io(s, adj->Action, adj->resource.io.BasePort, end);
-	}
-	return CS_UNSUPPORTED_FUNCTION;
-}
-
 #ifdef CONFIG_PCI
 static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
 {
@@ -889,7 +874,8 @@
 	.adjust_io_region = nonstatic_adjust_io_region,
 	.find_io = nonstatic_find_io_region,
 	.find_mem = nonstatic_find_mem_region,
-	.adjust_resource = nonstatic_adjust_resource_info,
+	.add_io = adjust_io,
+	.add_mem = adjust_memory,
 	.init = nonstatic_init,
 	.exit = nonstatic_release_resource_db,
 };
@@ -1008,41 +994,34 @@
 }
 static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
 
-static struct device_attribute *pccard_rsrc_attributes[] = {
-	&dev_attr_available_resources_io,
-	&dev_attr_available_resources_mem,
+static struct attribute *pccard_rsrc_attributes[] = {
+	&dev_attr_available_resources_io.attr,
+	&dev_attr_available_resources_mem.attr,
 	NULL,
 };
 
+static const struct attribute_group rsrc_attributes = {
+	.attrs = pccard_rsrc_attributes,
+};
+
 static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
 					   struct class_interface *class_intf)
 {
 	struct pcmcia_socket *s = dev_get_drvdata(dev);
-	struct device_attribute **attr;
-	int ret = 0;
+
 	if (s->resource_ops != &pccard_nonstatic_ops)
 		return 0;
-
-	for (attr = pccard_rsrc_attributes; *attr; attr++) {
-		ret = device_create_file(dev, *attr);
-		if (ret)
-			break;
-	}
-
-	return ret;
+	return sysfs_create_group(&dev->kobj, &rsrc_attributes);
 }
 
 static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
 					       struct class_interface *class_intf)
 {
 	struct pcmcia_socket *s = dev_get_drvdata(dev);
-	struct device_attribute **attr;
 
 	if (s->resource_ops != &pccard_nonstatic_ops)
 		return;
-
-	for (attr = pccard_rsrc_attributes; *attr; attr++)
-		device_remove_file(dev, *attr);
+	sysfs_remove_group(&dev->kobj, &rsrc_attributes);
 }
 
 static struct class_interface pccard_rsrc_interface __refdata = {
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h
index 1edc1da..91ef6a0 100644
--- a/drivers/pcmcia/soc_common.h
+++ b/drivers/pcmcia/soc_common.h
@@ -14,7 +14,6 @@
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"
 
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index 562384d..006a29e 100644
--- a/drivers/pcmcia/socket_sysfs.c
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -27,11 +27,9 @@
 #include <asm/system.h>
 #include <asm/irq.h>
 
-#define IN_CARD_SERVICES
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
 #include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
@@ -293,7 +291,7 @@
 		count = 0;
 	else {
 		struct pcmcia_socket *s;
-		cisinfo_t cisinfo;
+		unsigned int chains;
 
 		if (off + count > size)
 			count = size - off;
@@ -302,9 +300,9 @@
 
 		if (!(s->state & SOCKET_PRESENT))
 			return -ENODEV;
-		if (pccard_validate_cis(s, BIND_FN_ALL, &cisinfo))
+		if (pccard_validate_cis(s, BIND_FN_ALL, &chains))
 			return -EIO;
-		if (!cisinfo.Chains)
+		if (!chains)
 			return -ENODATA;
 
 		count = pccard_extract_cis(s, buf, off, count);
diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h
index d29657b..129db7b 100644
--- a/drivers/pcmcia/ti113x.h
+++ b/drivers/pcmcia/ti113x.h
@@ -155,7 +155,7 @@
 #define ENE_TEST_C9_TLTENABLE		0x02
 #define ENE_TEST_C9_PFENABLE_F0		0x04
 #define ENE_TEST_C9_PFENABLE_F1		0x08
-#define ENE_TEST_C9_PFENABLE		(ENE_TEST_C9_PFENABLE_F0 | ENE_TEST_C9_PFENABLE_F0)
+#define ENE_TEST_C9_PFENABLE		(ENE_TEST_C9_PFENABLE_F0 | ENE_TEST_C9_PFENABLE_F1)
 #define ENE_TEST_C9_WPDISALBLE_F0	0x40
 #define ENE_TEST_C9_WPDISALBLE_F1	0x80
 #define ENE_TEST_C9_WPDISALBLE		(ENE_TEST_C9_WPDISALBLE_F0 | ENE_TEST_C9_WPDISALBLE_F1)
@@ -692,7 +692,7 @@
 		goto out;
 
 	/* check state */
-	yenta_get_status(&socket->socket, &state);
+	yenta_get_status(&slot2->socket, &state);
 	if (state & SS_DETECT) {
 		ret = 0;
 		goto out;
diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c
index 2ef8cdf..90b9a65 100644
--- a/drivers/rtc/rtc-at32ap700x.c
+++ b/drivers/rtc/rtc-at32ap700x.c
@@ -265,6 +265,7 @@
 	}
 
 	platform_set_drvdata(pdev, rtc);
+	device_init_wakeup(&pdev->dev, 1);
 
 	dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
 			(unsigned long)rtc->regs, rtc->irq);
@@ -284,6 +285,8 @@
 {
 	struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev);
 
+	device_init_wakeup(&pdev->dev, 0);
+
 	free_irq(rtc->irq, rtc);
 	iounmap(rtc->regs);
 	rtc_device_unregister(rtc->rtc);
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 1a40256..1b6c52e 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -995,14 +995,14 @@
 	now = get_clock();
 
 	DBF_EVENT(DBF_ERR, "Interrupt: bus_id %s CS/DS %04x ip %08x",
-		  cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat),
-		  (unsigned int) intparm);
+		  cdev->dev.bus_id, ((irb->scsw.cmd.cstat << 8) |
+		  irb->scsw.cmd.dstat), (unsigned int) intparm);
 
 	/* check for unsolicited interrupts */
 	cqr = (struct dasd_ccw_req *) intparm;
-	if (!cqr || ((irb->scsw.cc == 1) &&
-		     (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
-		     (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) {
+	if (!cqr || ((irb->scsw.cmd.cc == 1) &&
+		     (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
+		     (irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND))) {
 		if (cqr && cqr->status == DASD_CQR_IN_IO)
 			cqr->status = DASD_CQR_QUEUED;
 		device = dasd_device_from_cdev_locked(cdev);
@@ -1025,7 +1025,7 @@
 
 	/* Check for clear pending */
 	if (cqr->status == DASD_CQR_CLEAR_PENDING &&
-	    irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
+	    irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
 		cqr->status = DASD_CQR_CLEARED;
 		dasd_device_clear_timer(device);
 		wake_up(&dasd_flush_wq);
@@ -1041,11 +1041,11 @@
 		return;
 	}
 	DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p",
-		      ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
+		      ((irb->scsw.cmd.cstat << 8) | irb->scsw.cmd.dstat), cqr);
 	next = NULL;
 	expires = 0;
-	if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
-	    irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) {
+	if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
+	    irb->scsw.cmd.cstat == 0 && !irb->esw.esw0.erw.cons) {
 		/* request was completed successfully */
 		cqr->status = DASD_CQR_SUCCESS;
 		cqr->stopclk = now;
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index e6700df..5c6e6f3 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -1572,7 +1572,7 @@
 
 	/* determine the address of the CCW to be restarted */
 	/* Imprecise ending is not set -> addr from IRB-SCSW */
-	cpa = default_erp->refers->irb.scsw.cpa;
+	cpa = default_erp->refers->irb.scsw.cmd.cpa;
 
 	if (cpa == 0) {
 
@@ -1725,7 +1725,7 @@
 
 	/* determine the address of the CCW to be restarted */
 	/* Imprecise ending is not set -> addr from IRB-SCSW */
-	cpa = previous_erp->irb.scsw.cpa;
+	cpa = previous_erp->irb.scsw.cmd.cpa;
 
 	if (cpa == 0) {
 
@@ -2171,7 +2171,7 @@
 {
 	struct dasd_device *device = erp->startdev;
 
-	if (erp->refers->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK
+	if (erp->refers->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK
 					   | SCHN_STAT_CHN_CTRL_CHK)) {
 		DEV_MESSAGE(KERN_DEBUG, device, "%s",
 			    "channel or interface control check");
@@ -2352,9 +2352,9 @@
 
 	if ((cqr1->irb.esw.esw0.erw.cons == 0) &&
 	    (cqr2->irb.esw.esw0.erw.cons == 0))	{
-		if ((cqr1->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK |
+		if ((cqr1->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK |
 					     SCHN_STAT_CHN_CTRL_CHK)) ==
-		    (cqr2->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK |
+		    (cqr2->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK |
 					     SCHN_STAT_CHN_CTRL_CHK)))
 			return 1; /* match with ifcc*/
 	}
@@ -2622,8 +2622,9 @@
 	}
 
 	/* double-check if current erp/cqr was successfull */
-	if ((cqr->irb.scsw.cstat == 0x00) &&
-	    (cqr->irb.scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) {
+	if ((cqr->irb.scsw.cmd.cstat == 0x00) &&
+	    (cqr->irb.scsw.cmd.dstat ==
+	     (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
 
 		DEV_MESSAGE(KERN_DEBUG, device,
 			    "ERP called for successful request %p"
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index a0edae0..e0b7721 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1404,13 +1404,14 @@
 
 	/* first of all check for state change pending interrupt */
 	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
-	if ((irb->scsw.dstat & mask) == mask) {
+	if ((irb->scsw.cmd.dstat & mask) == mask) {
 		dasd_generic_handle_state_change(device);
 		return;
 	}
 
 	/* summary unit check */
-	if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) {
+	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
+	    (irb->ecw[7] == 0x0D)) {
 		dasd_alias_handle_summary_unit_check(device, irb);
 		return;
 	}
@@ -2068,11 +2069,11 @@
 		      device->cdev->dev.bus_id);
 	len += sprintf(page + len, KERN_ERR PRINTK_HEADER
 		       " in req: %p CS: 0x%02X DS: 0x%02X\n", req,
-		       irb->scsw.cstat, irb->scsw.dstat);
+		       irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
 	len += sprintf(page + len, KERN_ERR PRINTK_HEADER
 		       " device %s: Failing CCW: %p\n",
 		       device->cdev->dev.bus_id,
-		       (void *) (addr_t) irb->scsw.cpa);
+		       (void *) (addr_t) irb->scsw.cmd.cpa);
 	if (irb->esw.esw0.erw.cons) {
 		for (sl = 0; sl < 4; sl++) {
 			len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -2122,7 +2123,8 @@
 		/* scsw->cda is either valid or zero  */
 		len = 0;
 		from = ++to;
-		fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+		fail = (struct ccw1 *)(addr_t)
+				irb->scsw.cmd.cpa; /* failing CCW */
 		if (from <  fail - 2) {
 			from = fail - 2;     /* there is a gap - print header */
 			len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 1166115..aee4656 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -222,7 +222,7 @@
 
 	/* first of all check for state change pending interrupt */
 	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
-	if ((irb->scsw.dstat & mask) == mask) {
+	if ((irb->scsw.cmd.dstat & mask) == mask) {
 		dasd_generic_handle_state_change(device);
 		return;
 	}
@@ -449,11 +449,11 @@
 		      device->cdev->dev.bus_id);
 	len += sprintf(page + len, KERN_ERR PRINTK_HEADER
 		       " in req: %p CS: 0x%02X DS: 0x%02X\n", req,
-		       irb->scsw.cstat, irb->scsw.dstat);
+		       irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
 	len += sprintf(page + len, KERN_ERR PRINTK_HEADER
 		       " device %s: Failing CCW: %p\n",
 		       device->cdev->dev.bus_id,
-		       (void *) (addr_t) irb->scsw.cpa);
+		       (void *) (addr_t) irb->scsw.cmd.cpa);
 	if (irb->esw.esw0.erw.cons) {
 		for (sl = 0; sl < 4; sl++) {
 			len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -498,11 +498,11 @@
 
 	/* print failing CCW area */
 	len = 0;
-	if (act <  ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
-		act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
+	if (act <  ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) {
+		act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2;
 		len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
 	}
-	end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
+	end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last);
 	while (act <= end) {
 		len += sprintf(page + len, KERN_ERR PRINTK_HEADER
 			       " CCW %p: %08X %08X DAT:",
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index bb52d2f..01fcdd9 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -167,10 +167,8 @@
 	struct dcssblk_dev_info *dev_info;
 	int rc;
 
-	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
-		PRINT_WARN("Invalid value, must be 0 or 1\n");
+	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
 		return -EINVAL;
-	}
 	down_write(&dcssblk_devices_sem);
 	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
 	if (atomic_read(&dev_info->use_count)) {
@@ -215,7 +213,6 @@
 			set_disk_ro(dev_info->gd, 0);
 		}
 	} else {
-		PRINT_WARN("Invalid value, must be 0 or 1\n");
 		rc = -EINVAL;
 		goto out;
 	}
@@ -258,10 +255,8 @@
 {
 	struct dcssblk_dev_info *dev_info;
 
-	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) {
-		PRINT_WARN("Invalid value, must be 0 or 1\n");
+	if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
 		return -EINVAL;
-	}
 	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
 
 	down_write(&dcssblk_devices_sem);
@@ -289,7 +284,6 @@
 		}
 	} else {
 		up_write(&dcssblk_devices_sem);
-		PRINT_WARN("Invalid value, must be 0 or 1\n");
 		return -EINVAL;
 	}
 	up_write(&dcssblk_devices_sem);
@@ -441,7 +435,6 @@
 	goto out;
 
 unregister_dev:
-	PRINT_ERR("device_create_file() failed!\n");
 	list_del(&dev_info->lh);
 	blk_cleanup_queue(dev_info->dcssblk_queue);
 	dev_info->gd->queue = NULL;
@@ -702,10 +695,8 @@
 static void __exit
 dcssblk_exit(void)
 {
-	PRINT_DEBUG("DCSSBLOCK EXIT...\n");
 	s390_root_dev_unregister(dcssblk_root_dev);
 	unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
-	PRINT_DEBUG("...finished!\n");
 }
 
 static int __init
@@ -713,27 +704,21 @@
 {
 	int rc;
 
-	PRINT_DEBUG("DCSSBLOCK INIT...\n");
 	dcssblk_root_dev = s390_root_dev_register("dcssblk");
-	if (IS_ERR(dcssblk_root_dev)) {
-		PRINT_ERR("device_register() failed!\n");
+	if (IS_ERR(dcssblk_root_dev))
 		return PTR_ERR(dcssblk_root_dev);
-	}
 	rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
 	if (rc) {
-		PRINT_ERR("device_create_file(add) failed!\n");
 		s390_root_dev_unregister(dcssblk_root_dev);
 		return rc;
 	}
 	rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
 	if (rc) {
-		PRINT_ERR("device_create_file(remove) failed!\n");
 		s390_root_dev_unregister(dcssblk_root_dev);
 		return rc;
 	}
 	rc = register_blkdev(0, DCSSBLK_NAME);
 	if (rc < 0) {
-		PRINT_ERR("Can't get dynamic major!\n");
 		s390_root_dev_unregister(dcssblk_root_dev);
 		return rc;
 	}
@@ -742,7 +727,6 @@
 
 	dcssblk_check_params();
 
-	PRINT_DEBUG("...finished!\n");
 	return 0;
 }
 
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index f231bc2..dd9b986 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -100,15 +100,10 @@
 		: "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
 	if (cc == 3)
 		return -ENXIO;
-	if (cc == 2) {
-		PRINT_ERR("expanded storage lost!\n");
+	if (cc == 2)
 		return -ENXIO;
-	}
-	if (cc == 1) {
-		PRINT_ERR("page in failed for page index %u.\n",
-			  xpage_index);
+	if (cc == 1)
 		return -EIO;
-	}
 	return 0;
 }
 
@@ -135,15 +130,10 @@
 		: "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
 	if (cc == 3)
 		return -ENXIO;
-	if (cc == 2) {
-		PRINT_ERR("expanded storage lost!\n");
+	if (cc == 2)
 		return -ENXIO;
-	}
-	if (cc == 1) {
-		PRINT_ERR("page out failed for page index %u.\n",
-			  xpage_index);
+	if (cc == 1)
 		return -EIO;
-	}
 	return 0;
 }
 
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 3e5653c..d3ec9b5 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -93,9 +93,6 @@
 	struct raw3215_req *queued_write;/* pointer to queued write requests */
 	wait_queue_head_t empty_wait; /* wait queue for flushing */
 	struct timer_list timer;      /* timer for delayed output */
-	char *message;		      /* pending message from raw3215_irq */
-	int msg_dstat;		      /* dstat for pending message */
-	int msg_cstat;		      /* cstat for pending message */
 	int line_pos;		      /* position on the line (for tabs) */
 	char ubuffer[80];	      /* copy_from_user buffer */
 };
@@ -359,11 +356,6 @@
 	raw3215_mk_write_req(raw);
 	raw3215_try_io(raw);
 	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);
-	/* Check for pending message from raw3215_irq */
-	if (raw->message != NULL) {
-		printk(raw->message, raw->msg_dstat, raw->msg_cstat);
-		raw->message = NULL;
-	}
 	tty = raw->tty;
 	if (tty != NULL &&
 	    RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) {
@@ -381,20 +373,14 @@
 	struct raw3215_req *req;
 	struct tty_struct *tty;
 	int cstat, dstat;
-	int count, slen;
+	int count;
 
 	raw = cdev->dev.driver_data;
 	req = (struct raw3215_req *) intparm;
-	cstat = irb->scsw.cstat;
-	dstat = irb->scsw.dstat;
-	if (cstat != 0) {
-		raw->message = KERN_WARNING
-			"Got nonzero channel status in raw3215_irq "
-			"(dev sts 0x%2x, sch sts 0x%2x)";
-		raw->msg_dstat = dstat;
-		raw->msg_cstat = cstat;
+	cstat = irb->scsw.cmd.cstat;
+	dstat = irb->scsw.cmd.dstat;
+	if (cstat != 0)
 		tasklet_schedule(&raw->tasklet);
-	}
 	if (dstat & 0x01) { /* we got a unit exception */
 		dstat &= ~0x01;	 /* we can ignore it */
 	}
@@ -404,8 +390,6 @@
 			break;
 		/* Attention interrupt, someone hit the enter key */
 		raw3215_mk_read_req(raw);
-		if (MACHINE_IS_P390)
-			memset(raw->inbuf, 0, RAW3215_INBUF_SIZE);
 		tasklet_schedule(&raw->tasklet);
 		break;
 	case 0x08:
@@ -415,7 +399,7 @@
 			return;		     /* That shouldn't happen ... */
 		if (req->type == RAW3215_READ) {
 			/* store residual count, then wait for device end */
-			req->residual = irb->scsw.count;
+			req->residual = irb->scsw.cmd.count;
 		}
 		if (dstat == 0x08)
 			break;
@@ -428,11 +412,6 @@
 
 			tty = raw->tty;
 			count = 160 - req->residual;
-			if (MACHINE_IS_P390) {
-				slen = strnlen(raw->inbuf, RAW3215_INBUF_SIZE);
-				if (count > slen)
-					count = slen;
-			} else
 			EBCASC(raw->inbuf, count);
 			cchar = ctrlchar_handle(raw->inbuf, count, tty);
 			switch (cchar & CTRLCHAR_MASK) {
@@ -481,11 +460,6 @@
 			raw->flags &= ~RAW3215_WORKING;
 			raw3215_free_req(req);
 		}
-		raw->message = KERN_WARNING
-			"Spurious interrupt in in raw3215_irq "
-			"(dev sts 0x%2x, sch sts 0x%2x)";
-		raw->msg_dstat = dstat;
-		raw->msg_cstat = cstat;
 		tasklet_schedule(&raw->tasklet);
 	}
 	return;
@@ -883,7 +857,6 @@
 		free_bootmem((unsigned long) raw->buffer, RAW3215_BUFFER_SIZE);
 		free_bootmem((unsigned long) raw, sizeof(struct raw3215_info));
 		raw3215[0] = NULL;
-		printk("Couldn't find a 3215 console device\n");
 		return -ENODEV;
 	}
 	register_console(&con3215);
@@ -1157,7 +1130,6 @@
 	tty_set_operations(driver, &tty3215_ops);
 	ret = tty_register_driver(driver);
 	if (ret) {
-		printk("Couldn't register tty3215 driver\n");
 		put_tty_driver(driver);
 		return ret;
 	}
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 0b04055..3c07974 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -411,15 +411,15 @@
 con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
 {
 	/* Handle ATTN. Schedule tasklet to read aid. */
-	if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
 		con3270_issue_read(cp);
 
 	if (rq) {
-		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 			rq->rc = -EIO;
 		else
 			/* Normal end. Copy residual count. */
-			rq->rescnt = irb->scsw.count;
+			rq->rescnt = irb->scsw.cmd.count;
 	}
 	return RAW3270_IO_DONE;
 }
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index ef36f213..e136d10 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -216,17 +216,17 @@
 fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
 {
 	/* Handle ATTN. Set indication and wake waiters for attention. */
-	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 		fp->attention = 1;
 		wake_up(&fp->wait);
 	}
 
 	if (rq) {
-		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 			rq->rc = -EIO;
 		else
 			/* Normal end. Copy residual count. */
-			rq->rescnt = irb->scsw.count;
+			rq->rescnt = irb->scsw.cmd.count;
 	}
 	return RAW3270_IO_DONE;
 }
@@ -512,11 +512,8 @@
 	int rc;
 
 	rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops);
-	if (rc) {
-		printk(KERN_ERR "fs3270 can't get major number %d: errno %d\n",
-		       IBM_FS3270_MAJOR, rc);
+	if (rc)
 		return rc;
-	}
 	return 0;
 }
 
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 1e1f506..f0e4c96 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -3,9 +3,8 @@
  *
  * Character device driver for reading z/VM *MONITOR service records.
  *
- * Copyright 2004 IBM Corporation, IBM Deutschland Entwicklung GmbH.
- *
- * Author: Gerald Schaefer <geraldsc@de.ibm.com>
+ *   Copyright IBM Corp. 2004, 2008
+ *   Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
  */
 
 #include <linux/module.h>
@@ -18,12 +17,11 @@
 #include <linux/ctype.h>
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <net/iucv/iucv.h>
 #include <asm/uaccess.h>
 #include <asm/ebcdic.h>
 #include <asm/extmem.h>
-#include <linux/poll.h>
-#include <net/iucv/iucv.h>
-
 
 //#define MON_DEBUG			/* Debug messages on/off */
 
@@ -152,10 +150,7 @@
 	    (mon_mca_end(monmsg) > mon_dcss_end) ||
 	    (mon_mca_start(monmsg) < mon_dcss_start) ||
 	    ((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0)))
-	{
-		P_DEBUG("READ, IGNORED INVALID MCA\n\n");
 		return -EINVAL;
-	}
 	return 0;
 }
 
@@ -164,10 +159,6 @@
 {
 	int rc;
 
-	P_DEBUG("read, REPLY: pathid = 0x%04X, msgid = 0x%08X, trgcls = "
-		"0x%08X\n\n",
-		monpriv->path->pathid, monmsg->msg.id, monmsg->msg.class);
-
 	rc = iucv_message_reply(monpriv->path, &monmsg->msg,
 				IUCV_IPRMDATA, NULL, 0);
 	atomic_dec(&monpriv->msglim_count);
@@ -202,15 +193,12 @@
 	struct mon_private *monpriv;
 
 	monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
-	if (!monpriv) {
-		P_ERROR("no memory for monpriv\n");
+	if (!monpriv)
 		return NULL;
-	}
 	for (i = 0; i < MON_MSGLIM; i++) {
 		monpriv->msg_array[i] = kzalloc(sizeof(struct mon_msg),
 						    GFP_KERNEL);
 		if (!monpriv->msg_array[i]) {
-			P_ERROR("open, no memory for msg_array\n");
 			mon_free_mem(monpriv);
 			return NULL;
 		}
@@ -218,41 +206,10 @@
 	return monpriv;
 }
 
-static inline void mon_read_debug(struct mon_msg *monmsg,
-				  struct mon_private *monpriv)
-{
-#ifdef MON_DEBUG
-	u8 msg_type[2], mca_type;
-	unsigned long records_len;
-
-	records_len = mon_rec_end(monmsg) - mon_rec_start(monmsg) + 1;
-
-	memcpy(msg_type, &monmsg->msg.class, 2);
-	EBCASC(msg_type, 2);
-	mca_type = mon_mca_type(monmsg, 0);
-	EBCASC(&mca_type, 1);
-
-	P_DEBUG("read, mon_read_index = %i, mon_write_index = %i\n",
-		monpriv->read_index, monpriv->write_index);
-	P_DEBUG("read, pathid = 0x%04X, msgid = 0x%08X, trgcls = 0x%08X\n",
-		monpriv->path->pathid, monmsg->msg.id, monmsg->msg.class);
-	P_DEBUG("read, msg_type = '%c%c', mca_type = '%c' / 0x%X / 0x%X\n",
-		msg_type[0], msg_type[1], mca_type ? mca_type : 'X',
-		mon_mca_type(monmsg, 1), mon_mca_type(monmsg, 2));
-	P_DEBUG("read, MCA: start = 0x%lX, end = 0x%lX\n",
-		mon_mca_start(monmsg), mon_mca_end(monmsg));
-	P_DEBUG("read, REC: start = 0x%X, end = 0x%X, len = %lu\n\n",
-		mon_rec_start(monmsg), mon_rec_end(monmsg), records_len);
-	if (mon_mca_size(monmsg) > 12)
-		P_DEBUG("READ, MORE THAN ONE MCA\n\n");
-#endif
-}
-
 static inline void mon_next_mca(struct mon_msg *monmsg)
 {
 	if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12))
 		return;
-	P_DEBUG("READ, NEXT MCA\n\n");
 	monmsg->mca_offset += 12;
 	monmsg->pos = 0;
 }
@@ -269,7 +226,6 @@
 		monmsg->msglim_reached = 0;
 		monmsg->pos = 0;
 		monmsg->mca_offset = 0;
-		P_WARNING("read, message limit reached\n");
 		monpriv->read_index = (monpriv->read_index + 1) %
 				      MON_MSGLIM;
 		atomic_dec(&monpriv->read_ready);
@@ -286,10 +242,6 @@
 {
 	struct mon_private *monpriv = path->private;
 
-	P_DEBUG("IUCV connection completed\n");
-	P_DEBUG("IUCV ACCEPT (from *MONITOR): Version = 0x%02X, Event = "
-		"0x%02X, Sample = 0x%02X\n",
-		ipuser[0], ipuser[1], ipuser[2]);
 	atomic_set(&monpriv->iucv_connected, 1);
 	wake_up(&mon_conn_wait_queue);
 }
@@ -310,7 +262,6 @@
 {
 	struct mon_private *monpriv = path->private;
 
-	P_DEBUG("IUCV message pending\n");
 	memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
 	       msg, sizeof(*msg));
 	if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
@@ -375,7 +326,6 @@
 		rc = -EIO;
 		goto out_path;
 	}
-	P_INFO("open, established connection to *MONITOR service\n\n");
 	filp->private_data = monpriv;
 	return nonseekable_open(inode, filp);
 
@@ -400,8 +350,6 @@
 	rc = iucv_path_sever(monpriv->path, user_data_sever);
 	if (rc)
 		P_ERROR("close, iucv_sever failed with rc = %i\n", rc);
-	else
-		P_INFO("close, terminated connection to *MONITOR service\n");
 
 	atomic_set(&monpriv->iucv_severed, 0);
 	atomic_set(&monpriv->iucv_connected, 0);
@@ -442,10 +390,8 @@
 		monmsg = monpriv->msg_array[monpriv->read_index];
 	}
 
-	if (!monmsg->pos) {
+	if (!monmsg->pos)
 		monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset;
-		mon_read_debug(monmsg, monpriv);
-	}
 	if (mon_check_mca(monmsg))
 		goto reply;
 
@@ -531,7 +477,6 @@
 		P_ERROR("failed to register with iucv driver\n");
 		return rc;
 	}
-	P_INFO("open, registered with IUCV\n");
 
 	rc = segment_type(mon_dcss_name);
 	if (rc < 0) {
@@ -555,13 +500,8 @@
 	dcss_mkname(mon_dcss_name, &user_data_connect[8]);
 
 	rc = misc_register(&mon_dev);
-	if (rc < 0 ) {
-		P_ERROR("misc_register failed, rc = %i\n", rc);
+	if (rc < 0 )
 		goto out;
-	}
-	P_INFO("Loaded segment %s from %p to %p, size = %lu Byte\n",
-		mon_dcss_name, (void *) mon_dcss_start, (void *) mon_dcss_end,
-		mon_dcss_end - mon_dcss_start + 1);
 	return 0;
 
 out:
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index 848ef7e..81a96e0 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -153,19 +153,10 @@
 	struct raw3270_request *rq;
 
 	rq = alloc_bootmem_low(sizeof(struct raw3270));
-	if (!rq)
-		return ERR_PTR(-ENOMEM);
-	memset(rq, 0, sizeof(struct raw3270_request));
 
 	/* alloc output buffer. */
-	if (size > 0) {
+	if (size > 0)
 		rq->buffer = alloc_bootmem_low(size);
-		if (!rq->buffer) {
-			free_bootmem((unsigned long) rq,
-				     sizeof(struct raw3270));
-			return ERR_PTR(-ENOMEM);
-		}
-	}
 	rq->size = size;
 	INIT_LIST_HEAD(&rq->list);
 
@@ -372,17 +363,17 @@
 
 	if (IS_ERR(irb))
 		rc = RAW3270_IO_RETRY;
-	else if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
+	else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
 		rq->rc = -EIO;
 		rc = RAW3270_IO_DONE;
-	} else if (irb->scsw.dstat ==  (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
-					DEV_STAT_UNIT_EXCEP)) {
+	} else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
+					   DEV_STAT_UNIT_EXCEP)) {
 		/* Handle CE-DE-UE and subsequent UDE */
 		set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
 		rc = RAW3270_IO_BUSY;
 	} else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) {
 		/* Wait for UDE if busy flag is set. */
-		if (irb->scsw.dstat & DEV_STAT_DEV_END) {
+		if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
 			clear_bit(RAW3270_FLAGS_BUSY, &rp->flags);
 			/* Got it, now retry. */
 			rc = RAW3270_IO_RETRY;
@@ -497,7 +488,7 @@
 	 * Unit-Check Processing:
 	 * Expect Command Reject or Intervention Required.
 	 */
-	if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
 		/* Request finished abnormally. */
 		if (irb->ecw[0] & SNS0_INTERVENTION_REQ) {
 			set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags);
@@ -505,16 +496,16 @@
 		}
 	}
 	if (rq) {
-		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
 			if (irb->ecw[0] & SNS0_CMD_REJECT)
 				rq->rc = -EOPNOTSUPP;
 			else
 				rq->rc = -EIO;
 		} else
 			/* Request finished normally. Copy residual count. */
-			rq->rescnt = irb->scsw.count;
+			rq->rescnt = irb->scsw.cmd.count;
 	}
-	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 		set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags);
 		wake_up(&raw3270_wait_queue);
 	}
@@ -619,7 +610,6 @@
 		rp->cols = 132;
 		break;
 	default:
-		printk(KERN_WARNING "vrdccrmd is 0x%.8x\n", model);
 		rc = -EOPNOTSUPP;
 		break;
 	}
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 2c7a1ee..3c8b25e 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -506,6 +506,8 @@
 	if (scbuf->validity_sclp_send_mask)
 		sclp_send_mask = scbuf->sclp_send_mask;
 	spin_unlock_irqrestore(&sclp_lock, flags);
+	if (scbuf->validity_sclp_active_facility_mask)
+		sclp_facilities = scbuf->sclp_active_facility_mask;
 	sclp_dispatch_state_change();
 }
 
@@ -782,11 +784,9 @@
 	/* Is this the interrupt we are waiting for? */
 	if (finished_sccb == 0)
 		return;
-	if (finished_sccb != (u32) (addr_t) sclp_init_sccb) {
-		printk(KERN_WARNING SCLP_HEADER "unsolicited interrupt "
-		       "for buffer at 0x%x\n", finished_sccb);
-		return;
-	}
+	if (finished_sccb != (u32) (addr_t) sclp_init_sccb)
+		panic("sclp: unsolicited interrupt for buffer at 0x%x\n",
+		      finished_sccb);
 	spin_lock(&sclp_lock);
 	if (sclp_running_state == sclp_running_state_running) {
 		sclp_init_req.status = SCLP_REQ_DONE;
@@ -883,8 +883,6 @@
 	unsigned long flags;
 	int rc;
 
-	if (!MACHINE_HAS_SCLP)
-		return -ENODEV;
 	spin_lock_irqsave(&sclp_lock, flags);
 	/* Check for previous or running initialization */
 	if (sclp_init_state != sclp_init_state_uninitialized) {
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index b5c2339..0c2b774 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -11,6 +11,9 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/memory.h>
 #include <asm/chpid.h>
 #include <asm/sclp.h>
 #include "sclp.h"
@@ -43,6 +46,8 @@
 
 u64 sclp_facilities;
 static u8 sclp_fac84;
+static unsigned long long rzm;
+static unsigned long long rnmax;
 
 static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
 {
@@ -62,7 +67,7 @@
 	return rc;
 }
 
-void __init sclp_read_info_early(void)
+static void __init sclp_read_info_early(void)
 {
 	int rc;
 	int i;
@@ -92,34 +97,33 @@
 
 void __init sclp_facilities_detect(void)
 {
-	if (!early_read_info_sccb_valid)
-		return;
-	sclp_facilities = early_read_info_sccb.facilities;
-	sclp_fac84 = early_read_info_sccb.fac84;
-}
-
-unsigned long long __init sclp_memory_detect(void)
-{
-	unsigned long long memsize;
 	struct read_info_sccb *sccb;
 
+	sclp_read_info_early();
 	if (!early_read_info_sccb_valid)
-		return 0;
+		return;
+
 	sccb = &early_read_info_sccb;
-	if (sccb->rnsize)
-		memsize = sccb->rnsize << 20;
-	else
-		memsize = sccb->rnsize2 << 20;
-	if (sccb->rnmax)
-		memsize *= sccb->rnmax;
-	else
-		memsize *= sccb->rnmax2;
-	return memsize;
+	sclp_facilities = sccb->facilities;
+	sclp_fac84 = sccb->fac84;
+	rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
+	rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
+	rzm <<= 20;
+}
+
+unsigned long long sclp_get_rnmax(void)
+{
+	return rnmax;
+}
+
+unsigned long long sclp_get_rzm(void)
+{
+	return rzm;
 }
 
 /*
- * This function will be called after sclp_memory_detect(), which gets called
- * early from early.c code. Therefore the sccb should have valid contents.
+ * This function will be called after sclp_facilities_detect(), which gets
+ * called from early.c code. Therefore the sccb should have valid contents.
  */
 void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
 {
@@ -278,6 +282,305 @@
 	return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8);
 }
 
+#ifdef CONFIG_MEMORY_HOTPLUG
+
+static DEFINE_MUTEX(sclp_mem_mutex);
+static LIST_HEAD(sclp_mem_list);
+static u8 sclp_max_storage_id;
+static unsigned long sclp_storage_ids[256 / BITS_PER_LONG];
+
+struct memory_increment {
+	struct list_head list;
+	u16 rn;
+	int standby;
+	int usecount;
+};
+
+struct assign_storage_sccb {
+	struct sccb_header header;
+	u16 rn;
+} __packed;
+
+static unsigned long long rn2addr(u16 rn)
+{
+	return (unsigned long long) (rn - 1) * rzm;
+}
+
+static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
+{
+	struct assign_storage_sccb *sccb;
+	int rc;
+
+	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sccb)
+		return -ENOMEM;
+	sccb->header.length = PAGE_SIZE;
+	sccb->rn = rn;
+	rc = do_sync_request(cmd, sccb);
+	if (rc)
+		goto out;
+	switch (sccb->header.response_code) {
+	case 0x0020:
+	case 0x0120:
+		break;
+	default:
+		rc = -EIO;
+		break;
+	}
+out:
+	free_page((unsigned long) sccb);
+	return rc;
+}
+
+static int sclp_assign_storage(u16 rn)
+{
+	return do_assign_storage(0x000d0001, rn);
+}
+
+static int sclp_unassign_storage(u16 rn)
+{
+	return do_assign_storage(0x000c0001, rn);
+}
+
+struct attach_storage_sccb {
+	struct sccb_header header;
+	u16 :16;
+	u16 assigned;
+	u32 :32;
+	u32 entries[0];
+} __packed;
+
+static int sclp_attach_storage(u8 id)
+{
+	struct attach_storage_sccb *sccb;
+	int rc;
+	int i;
+
+	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sccb)
+		return -ENOMEM;
+	sccb->header.length = PAGE_SIZE;
+	rc = do_sync_request(0x00080001 | id << 8, sccb);
+	if (rc)
+		goto out;
+	switch (sccb->header.response_code) {
+	case 0x0020:
+		set_bit(id, sclp_storage_ids);
+		for (i = 0; i < sccb->assigned; i++)
+			sclp_unassign_storage(sccb->entries[i] >> 16);
+		break;
+	default:
+		rc = -EIO;
+		break;
+	}
+out:
+	free_page((unsigned long) sccb);
+	return rc;
+}
+
+static int sclp_mem_change_state(unsigned long start, unsigned long size,
+				 int online)
+{
+	struct memory_increment *incr;
+	unsigned long long istart;
+	int rc = 0;
+
+	list_for_each_entry(incr, &sclp_mem_list, list) {
+		istart = rn2addr(incr->rn);
+		if (start + size - 1 < istart)
+			break;
+		if (start > istart + rzm - 1)
+			continue;
+		if (online) {
+			if (incr->usecount++)
+				continue;
+			/*
+			 * Don't break the loop if one assign fails. Loop may
+			 * be walked again on CANCEL and we can't save
+			 * information if state changed before or not.
+			 * So continue and increase usecount for all increments.
+			 */
+			rc |= sclp_assign_storage(incr->rn);
+		} else {
+			if (--incr->usecount)
+				continue;
+			sclp_unassign_storage(incr->rn);
+		}
+	}
+	return rc ? -EIO : 0;
+}
+
+static int sclp_mem_notifier(struct notifier_block *nb,
+			     unsigned long action, void *data)
+{
+	unsigned long start, size;
+	struct memory_notify *arg;
+	unsigned char id;
+	int rc = 0;
+
+	arg = data;
+	start = arg->start_pfn << PAGE_SHIFT;
+	size = arg->nr_pages << PAGE_SHIFT;
+	mutex_lock(&sclp_mem_mutex);
+	for (id = 0; id <= sclp_max_storage_id; id++)
+		if (!test_bit(id, sclp_storage_ids))
+			sclp_attach_storage(id);
+	switch (action) {
+	case MEM_ONLINE:
+		break;
+	case MEM_GOING_ONLINE:
+		rc = sclp_mem_change_state(start, size, 1);
+		break;
+	case MEM_CANCEL_ONLINE:
+		sclp_mem_change_state(start, size, 0);
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	mutex_unlock(&sclp_mem_mutex);
+	return rc ? NOTIFY_BAD : NOTIFY_OK;
+}
+
+static struct notifier_block sclp_mem_nb = {
+	.notifier_call = sclp_mem_notifier,
+};
+
+static void __init add_memory_merged(u16 rn)
+{
+	static u16 first_rn, num;
+	unsigned long long start, size;
+
+	if (rn && first_rn && (first_rn + num == rn)) {
+		num++;
+		return;
+	}
+	if (!first_rn)
+		goto skip_add;
+	start = rn2addr(first_rn);
+	size = (unsigned long long ) num * rzm;
+	if (start >= VMEM_MAX_PHYS)
+		goto skip_add;
+	if (start + size > VMEM_MAX_PHYS)
+		size = VMEM_MAX_PHYS - start;
+	add_memory(0, start, size);
+skip_add:
+	first_rn = rn;
+	num = 1;
+}
+
+static void __init sclp_add_standby_memory(void)
+{
+	struct memory_increment *incr;
+
+	list_for_each_entry(incr, &sclp_mem_list, list)
+		if (incr->standby)
+			add_memory_merged(incr->rn);
+	add_memory_merged(0);
+}
+
+static void __init insert_increment(u16 rn, int standby, int assigned)
+{
+	struct memory_increment *incr, *new_incr;
+	struct list_head *prev;
+	u16 last_rn;
+
+	new_incr = kzalloc(sizeof(*new_incr), GFP_KERNEL);
+	if (!new_incr)
+		return;
+	new_incr->rn = rn;
+	new_incr->standby = standby;
+	last_rn = 0;
+	prev = &sclp_mem_list;
+	list_for_each_entry(incr, &sclp_mem_list, list) {
+		if (assigned && incr->rn > rn)
+			break;
+		if (!assigned && incr->rn - last_rn > 1)
+			break;
+		last_rn = incr->rn;
+		prev = &incr->list;
+	}
+	if (!assigned)
+		new_incr->rn = last_rn + 1;
+	if (new_incr->rn > rnmax) {
+		kfree(new_incr);
+		return;
+	}
+	list_add(&new_incr->list, prev);
+}
+
+struct read_storage_sccb {
+	struct sccb_header header;
+	u16 max_id;
+	u16 assigned;
+	u16 standby;
+	u16 :16;
+	u32 entries[0];
+} __packed;
+
+static int __init sclp_detect_standby_memory(void)
+{
+	struct read_storage_sccb *sccb;
+	int i, id, assigned, rc;
+
+	if (!early_read_info_sccb_valid)
+		return 0;
+	if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
+		return 0;
+	rc = -ENOMEM;
+	sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
+	if (!sccb)
+		goto out;
+	assigned = 0;
+	for (id = 0; id <= sclp_max_storage_id; id++) {
+		memset(sccb, 0, PAGE_SIZE);
+		sccb->header.length = PAGE_SIZE;
+		rc = do_sync_request(0x00040001 | id << 8, sccb);
+		if (rc)
+			goto out;
+		switch (sccb->header.response_code) {
+		case 0x0010:
+			set_bit(id, sclp_storage_ids);
+			for (i = 0; i < sccb->assigned; i++) {
+				if (!sccb->entries[i])
+					continue;
+				assigned++;
+				insert_increment(sccb->entries[i] >> 16, 0, 1);
+			}
+			break;
+		case 0x0310:
+			break;
+		case 0x0410:
+			for (i = 0; i < sccb->assigned; i++) {
+				if (!sccb->entries[i])
+					continue;
+				assigned++;
+				insert_increment(sccb->entries[i] >> 16, 1, 1);
+			}
+			break;
+		default:
+			rc = -EIO;
+			break;
+		}
+		if (!rc)
+			sclp_max_storage_id = sccb->max_id;
+	}
+	if (rc || list_empty(&sclp_mem_list))
+		goto out;
+	for (i = 1; i <= rnmax - assigned; i++)
+		insert_increment(0, 1, 0);
+	rc = register_memory_notifier(&sclp_mem_nb);
+	if (rc)
+		goto out;
+	sclp_add_standby_memory();
+out:
+	free_page((unsigned long) sccb);
+	return rc;
+}
+__initcall(sclp_detect_standby_memory);
+
+#endif /* CONFIG_MEMORY_HOTPLUG */
+
 /*
  * Channel path configuration related functions.
  */
diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c
index ead1043..7e619c5 100644
--- a/drivers/s390/char/sclp_con.c
+++ b/drivers/s390/char/sclp_con.c
@@ -14,14 +14,13 @@
 #include <linux/timer.h>
 #include <linux/jiffies.h>
 #include <linux/bootmem.h>
+#include <linux/termios.h>
 #include <linux/err.h>
 
 #include "sclp.h"
 #include "sclp_rw.h"
 #include "sclp_tty.h"
 
-#define SCLP_CON_PRINT_HEADER "sclp console driver: "
-
 #define sclp_console_major 4		/* TTYAUX_MAJOR */
 #define sclp_console_minor 64
 #define sclp_console_name  "ttyS"
@@ -222,8 +221,6 @@
 	INIT_LIST_HEAD(&sclp_con_pages);
 	for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
 		page = alloc_bootmem_low_pages(PAGE_SIZE);
-		if (page == NULL)
-			return -ENOMEM;
 		list_add_tail((struct list_head *) page, &sclp_con_pages);
 	}
 	INIT_LIST_HEAD(&sclp_con_outqueue);
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
index ad05a87..fff4ff4 100644
--- a/drivers/s390/char/sclp_config.c
+++ b/drivers/s390/char/sclp_config.c
@@ -8,6 +8,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/cpu.h>
+#include <linux/kthread.h>
 #include <linux/sysdev.h>
 #include <linux/workqueue.h>
 #include <asm/smp.h>
@@ -40,9 +41,19 @@
 	put_online_cpus();
 }
 
-static void __ref sclp_cpu_change_notify(struct work_struct *work)
+static int sclp_cpu_kthread(void *data)
 {
 	smp_rescan_cpus();
+	return 0;
+}
+
+static void __ref sclp_cpu_change_notify(struct work_struct *work)
+{
+	/* Can't call smp_rescan_cpus() from  workqueue context since it may
+	 * deadlock in case of cpu hotplug. So we have to create a kernel
+	 * thread in order to call it.
+	 */
+	kthread_run(sclp_cpu_kthread, NULL, "cpu_rescan");
 }
 
 static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
@@ -74,10 +85,8 @@
 	INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
 
 	rc = sclp_register(&sclp_conf_register);
-	if (rc) {
-		printk(KERN_ERR TAG "failed to register (%d).\n", rc);
+	if (rc)
 		return rc;
-	}
 
 	if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) {
 		printk(KERN_WARNING TAG "no configuration management.\n");
diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c
index 9f37456..d887bd2 100644
--- a/drivers/s390/char/sclp_cpi_sys.c
+++ b/drivers/s390/char/sclp_cpi_sys.c
@@ -27,6 +27,8 @@
 #define CPI_LENGTH_NAME 8
 #define CPI_LENGTH_LEVEL 16
 
+static DEFINE_MUTEX(sclp_cpi_mutex);
+
 struct cpi_evbuf {
 	struct evbuf_header header;
 	u8	id_format;
@@ -124,21 +126,15 @@
 	int response;
 
 	rc = sclp_register(&sclp_cpi_event);
-	if (rc) {
-		printk(KERN_WARNING "cpi: could not register "
-			"to hardware console.\n");
+	if (rc)
 		goto out;
-	}
 	if (!(sclp_cpi_event.sclp_receive_mask & EVTYP_CTLPROGIDENT_MASK)) {
-		printk(KERN_WARNING "cpi: no control program "
-			"identification support\n");
 		rc = -EOPNOTSUPP;
 		goto out_unregister;
 	}
 
 	req = cpi_prepare_req();
 	if (IS_ERR(req)) {
-		printk(KERN_WARNING "cpi: could not allocate request\n");
 		rc = PTR_ERR(req);
 		goto out_unregister;
 	}
@@ -148,10 +144,8 @@
 
 	/* Add request to sclp queue */
 	rc = sclp_add_request(req);
-	if (rc) {
-		printk(KERN_WARNING "cpi: could not start request\n");
+	if (rc)
 		goto out_free_req;
-	}
 
 	wait_for_completion(&completion);
 
@@ -223,7 +217,12 @@
 static ssize_t system_name_show(struct kobject *kobj,
 				struct kobj_attribute *attr, char *page)
 {
-	return snprintf(page, PAGE_SIZE, "%s\n", system_name);
+	int rc;
+
+	mutex_lock(&sclp_cpi_mutex);
+	rc = snprintf(page, PAGE_SIZE, "%s\n", system_name);
+	mutex_unlock(&sclp_cpi_mutex);
+	return rc;
 }
 
 static ssize_t system_name_store(struct kobject *kobj,
@@ -237,7 +236,9 @@
 	if (rc)
 		return rc;
 
+	mutex_lock(&sclp_cpi_mutex);
 	set_string(system_name, buf);
+	mutex_unlock(&sclp_cpi_mutex);
 
 	return len;
 }
@@ -248,7 +249,12 @@
 static ssize_t sysplex_name_show(struct kobject *kobj,
 				 struct kobj_attribute *attr, char *page)
 {
-	return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
+	int rc;
+
+	mutex_lock(&sclp_cpi_mutex);
+	rc = snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
+	mutex_unlock(&sclp_cpi_mutex);
+	return rc;
 }
 
 static ssize_t sysplex_name_store(struct kobject *kobj,
@@ -262,7 +268,9 @@
 	if (rc)
 		return rc;
 
+	mutex_lock(&sclp_cpi_mutex);
 	set_string(sysplex_name, buf);
+	mutex_unlock(&sclp_cpi_mutex);
 
 	return len;
 }
@@ -273,7 +281,12 @@
 static ssize_t system_type_show(struct kobject *kobj,
 				struct kobj_attribute *attr, char *page)
 {
-	return snprintf(page, PAGE_SIZE, "%s\n", system_type);
+	int rc;
+
+	mutex_lock(&sclp_cpi_mutex);
+	rc = snprintf(page, PAGE_SIZE, "%s\n", system_type);
+	mutex_unlock(&sclp_cpi_mutex);
+	return rc;
 }
 
 static ssize_t system_type_store(struct kobject *kobj,
@@ -287,7 +300,9 @@
 	if (rc)
 		return rc;
 
+	mutex_lock(&sclp_cpi_mutex);
 	set_string(system_type, buf);
+	mutex_unlock(&sclp_cpi_mutex);
 
 	return len;
 }
@@ -298,8 +313,11 @@
 static ssize_t system_level_show(struct kobject *kobj,
 				 struct kobj_attribute *attr, char *page)
 {
-	unsigned long long level = system_level;
+	unsigned long long level;
 
+	mutex_lock(&sclp_cpi_mutex);
+	level = system_level;
+	mutex_unlock(&sclp_cpi_mutex);
 	return snprintf(page, PAGE_SIZE, "%#018llx\n", level);
 }
 
@@ -320,8 +338,9 @@
 	if (*endp)
 		return -EINVAL;
 
+	mutex_lock(&sclp_cpi_mutex);
 	system_level = level;
-
+	mutex_unlock(&sclp_cpi_mutex);
 	return len;
 }
 
@@ -334,7 +353,9 @@
 {
 	int rc;
 
+	mutex_lock(&sclp_cpi_mutex);
 	rc = cpi_req();
+	mutex_unlock(&sclp_cpi_mutex);
 	if (rc)
 		return rc;
 
@@ -373,12 +394,16 @@
 	if (rc)
 		return rc;
 
+	mutex_lock(&sclp_cpi_mutex);
 	set_string(system_name, system);
 	set_string(sysplex_name, sysplex);
 	set_string(system_type, type);
 	system_level = level;
 
-	return cpi_req();
+	rc = cpi_req();
+	mutex_unlock(&sclp_cpi_mutex);
+
+	return rc;
 }
 EXPORT_SYMBOL(sclp_cpi_set_data);
 
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index 45ff25e..84c191c 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -51,13 +51,7 @@
 static int __init
 sclp_quiesce_init(void)
 {
-	int rc;
-
-	rc = sclp_register(&sclp_quiesce_event);
-	if (rc)
-		printk(KERN_WARNING "sclp: could not register quiesce handler "
-		       "(rc=%d)\n", rc);
-	return rc;
+	return sclp_register(&sclp_quiesce_event);
 }
 
 module_init(sclp_quiesce_init);
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index da09781..710af42 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -19,8 +19,6 @@
 #include "sclp.h"
 #include "sclp_rw.h"
 
-#define SCLP_RW_PRINT_HEADER "sclp low level driver: "
-
 /*
  * The room for the SCCB (only for writing) is not equal to a pages size
  * (as it is specified as the maximum size in the SCLP documentation)
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index 1c06497..8b85485 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -239,10 +239,8 @@
 	debug_register_view(sdias_dbf, &debug_sprintf_view);
 	debug_set_level(sdias_dbf, 6);
 	rc = sclp_register(&sclp_sdias_register);
-	if (rc) {
-		ERROR_MSG("sclp register failed\n");
+	if (rc)
 		return rc;
-	}
 	init_waitqueue_head(&sdias_wq);
 	TRACE("init done\n");
 	return 0;
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 40b1152..434ba04 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
-#include <linux/wait.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -25,8 +24,6 @@
 #include "sclp_rw.h"
 #include "sclp_tty.h"
 
-#define SCLP_TTY_PRINT_HEADER "sclp tty driver: "
-
 /*
  * size of a buffer that collects single characters coming in
  * via sclp_tty_put_char()
@@ -50,8 +47,6 @@
 static struct sclp_buffer *sclp_ttybuf;
 /* Timer for delayed output of console messages. */
 static struct timer_list sclp_tty_timer;
-/* Waitqueue to wait for buffers to get empty. */
-static wait_queue_head_t sclp_tty_waitq;
 
 static struct tty_struct *sclp_tty;
 static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
@@ -59,19 +54,11 @@
 
 struct tty_driver *sclp_tty_driver;
 
-static struct sclp_ioctls sclp_ioctls;
-static struct sclp_ioctls sclp_ioctls_init =
-{
-	8,			/* 1 hor. tab. = 8 spaces */
-	0,			/* no echo of input by this driver */
-	80,			/* 80 characters/line */
-	1,			/* write after 1/10 s without final new line */
-	MAX_KMEM_PAGES,		/* quick fix: avoid __alloc_pages */
-	MAX_KMEM_PAGES,		/* take 32/64 pages from kernel memory, */
-	0,			/* do not convert to lower case */
-	0x6c			/* to seprate upper and lower case */
-				/* ('%' in EBCDIC) */
-};
+static int sclp_tty_tolower;
+static int sclp_tty_columns = 80;
+
+#define SPACES_PER_TAB 8
+#define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */
 
 /* This routine is called whenever we try to open a SCLP terminal. */
 static int
@@ -92,136 +79,6 @@
 	sclp_tty = NULL;
 }
 
-/* execute commands to control the i/o behaviour of the SCLP tty at runtime */
-static int
-sclp_tty_ioctl(struct tty_struct *tty, struct file * file,
-	       unsigned int cmd, unsigned long arg)
-{
-	unsigned long flags;
-	unsigned int obuf;
-	int check;
-	int rc;
-
-	if (tty->flags & (1 << TTY_IO_ERROR))
-		return -EIO;
-	rc = 0;
-	check = 0;
-	switch (cmd) {
-	case TIOCSCLPSHTAB:
-		/* set width of horizontal tab	*/
-		if (get_user(sclp_ioctls.htab, (unsigned short __user *) arg))
-			rc = -EFAULT;
-		else
-			check = 1;
-		break;
-	case TIOCSCLPGHTAB:
-		/* get width of horizontal tab	*/
-		if (put_user(sclp_ioctls.htab, (unsigned short __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPSECHO:
-		/* enable/disable echo of input */
-		if (get_user(sclp_ioctls.echo, (unsigned char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPGECHO:
-		/* Is echo of input enabled ?  */
-		if (put_user(sclp_ioctls.echo, (unsigned char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPSCOLS:
-		/* set number of columns for output  */
-		if (get_user(sclp_ioctls.columns, (unsigned short __user *) arg))
-			rc = -EFAULT;
-		else
-			check = 1;
-		break;
-	case TIOCSCLPGCOLS:
-		/* get number of columns for output  */
-		if (put_user(sclp_ioctls.columns, (unsigned short __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPSNL:
-		/* enable/disable writing without final new line character  */
-		if (get_user(sclp_ioctls.final_nl, (signed char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPGNL:
-		/* Is writing without final new line character enabled ?  */
-		if (put_user(sclp_ioctls.final_nl, (signed char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPSOBUF:
-		/*
-		 * set the maximum buffers size for output, will be rounded
-		 * up to next 4kB boundary and stored as number of SCCBs
-		 * (4kB Buffers) limitation: 256 x 4kB
-		 */
-		if (get_user(obuf, (unsigned int __user *) arg) == 0) {
-			if (obuf & 0xFFF)
-				sclp_ioctls.max_sccb = (obuf >> 12) + 1;
-			else
-				sclp_ioctls.max_sccb = (obuf >> 12);
-		} else
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPGOBUF:
-		/* get the maximum buffers size for output  */
-		obuf = sclp_ioctls.max_sccb << 12;
-		if (put_user(obuf, (unsigned int __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPGKBUF:
-		/* get the number of buffers got from kernel at startup */
-		if (put_user(sclp_ioctls.kmem_sccb, (unsigned short __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPSCASE:
-		/* enable/disable conversion from upper to lower case */
-		if (get_user(sclp_ioctls.tolower, (unsigned char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPGCASE:
-		/* Is conversion from upper to lower case of input enabled? */
-		if (put_user(sclp_ioctls.tolower, (unsigned char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPSDELIM:
-		/*
-		 * set special character used for separating upper and
-		 * lower case, 0x00 disables this feature
-		 */
-		if (get_user(sclp_ioctls.delim, (unsigned char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPGDELIM:
-		/*
-		 * get special character used for separating upper and
-		 * lower case, 0x00 disables this feature
-		 */
-		if (put_user(sclp_ioctls.delim, (unsigned char __user *) arg))
-			rc = -EFAULT;
-		break;
-	case TIOCSCLPSINIT:
-		/* set initial (default) sclp ioctls  */
-		sclp_ioctls = sclp_ioctls_init;
-		check = 1;
-		break;
-	default:
-		rc = -ENOIOCTLCMD;
-		break;
-	}
-	if (check) {
-		spin_lock_irqsave(&sclp_tty_lock, flags);
-		if (sclp_ttybuf != NULL) {
-			sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab);
-			sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns);
-		}
-		spin_unlock_irqrestore(&sclp_tty_lock, flags);
-	}
-	return rc;
-}
-
 /*
  * This routine returns the numbers of characters the tty driver
  * will accept for queuing to be written.  This number is subject
@@ -268,7 +125,6 @@
 					    struct sclp_buffer, list);
 		spin_unlock_irqrestore(&sclp_tty_lock, flags);
 	} while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback));
-	wake_up(&sclp_tty_waitq);
 	/* check if the tty needs a wake up call */
 	if (sclp_tty != NULL) {
 		tty_wakeup(sclp_tty);
@@ -316,37 +172,37 @@
 /*
  * Write a string to the sclp tty.
  */
-static void
-sclp_tty_write_string(const unsigned char *str, int count)
+static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail)
 {
 	unsigned long flags;
 	void *page;
 	int written;
+	int overall_written;
 	struct sclp_buffer *buf;
 
 	if (count <= 0)
-		return;
+		return 0;
+	overall_written = 0;
 	spin_lock_irqsave(&sclp_tty_lock, flags);
 	do {
 		/* Create a sclp output buffer if none exists yet */
 		if (sclp_ttybuf == NULL) {
 			while (list_empty(&sclp_tty_pages)) {
 				spin_unlock_irqrestore(&sclp_tty_lock, flags);
-				if (in_interrupt())
-					sclp_sync_wait();
+				if (may_fail)
+					goto out;
 				else
-					wait_event(sclp_tty_waitq,
-						!list_empty(&sclp_tty_pages));
+					sclp_sync_wait();
 				spin_lock_irqsave(&sclp_tty_lock, flags);
 			}
 			page = sclp_tty_pages.next;
 			list_del((struct list_head *) page);
-			sclp_ttybuf = sclp_make_buffer(page,
-						       sclp_ioctls.columns,
-						       sclp_ioctls.htab);
+			sclp_ttybuf = sclp_make_buffer(page, sclp_tty_columns,
+						       SPACES_PER_TAB);
 		}
 		/* try to write the string to the current output buffer */
 		written = sclp_write(sclp_ttybuf, str, count);
+		overall_written += written;
 		if (written == count)
 			break;
 		/*
@@ -363,27 +219,17 @@
 		count -= written;
 	} while (count > 0);
 	/* Setup timer to output current console buffer after 1/10 second */
-	if (sclp_ioctls.final_nl) {
-		if (sclp_ttybuf != NULL &&
-		    sclp_chars_in_buffer(sclp_ttybuf) != 0 &&
-		    !timer_pending(&sclp_tty_timer)) {
-			init_timer(&sclp_tty_timer);
-			sclp_tty_timer.function = sclp_tty_timeout;
-			sclp_tty_timer.data = 0UL;
-			sclp_tty_timer.expires = jiffies + HZ/10;
-			add_timer(&sclp_tty_timer);
-		}
-	} else {
-		if (sclp_ttybuf != NULL &&
-		    sclp_chars_in_buffer(sclp_ttybuf) != 0) {
-			buf = sclp_ttybuf;
-			sclp_ttybuf = NULL;
-			spin_unlock_irqrestore(&sclp_tty_lock, flags);
-			__sclp_ttybuf_emit(buf);
-			spin_lock_irqsave(&sclp_tty_lock, flags);
-		}
+	if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) &&
+	    !timer_pending(&sclp_tty_timer)) {
+		init_timer(&sclp_tty_timer);
+		sclp_tty_timer.function = sclp_tty_timeout;
+		sclp_tty_timer.data = 0UL;
+		sclp_tty_timer.expires = jiffies + HZ/10;
+		add_timer(&sclp_tty_timer);
 	}
 	spin_unlock_irqrestore(&sclp_tty_lock, flags);
+out:
+	return overall_written;
 }
 
 /*
@@ -395,11 +241,10 @@
 sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
 {
 	if (sclp_tty_chars_count > 0) {
-		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
 		sclp_tty_chars_count = 0;
 	}
-	sclp_tty_write_string(buf, count);
-	return count;
+	return sclp_tty_write_string(buf, count, 1);
 }
 
 /*
@@ -417,9 +262,10 @@
 {
 	sclp_tty_chars[sclp_tty_chars_count++] = ch;
 	if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
-		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
 		sclp_tty_chars_count = 0;
-	} return 1;
+	}
+	return 1;
 }
 
 /*
@@ -430,7 +276,7 @@
 sclp_tty_flush_chars(struct tty_struct *tty)
 {
 	if (sclp_tty_chars_count > 0) {
-		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
 		sclp_tty_chars_count = 0;
 	}
 }
@@ -469,7 +315,7 @@
 sclp_tty_flush_buffer(struct tty_struct *tty)
 {
 	if (sclp_tty_chars_count > 0) {
-		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count);
+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
 		sclp_tty_chars_count = 0;
 	}
 }
@@ -517,9 +363,7 @@
  * modifiy original string,
  * returns length of resulting string
  */
-static int
-sclp_switch_cases(unsigned char *buf, int count,
-		  unsigned char delim, int tolower)
+static int sclp_switch_cases(unsigned char *buf, int count)
 {
 	unsigned char *ip, *op;
 	int toggle;
@@ -529,9 +373,9 @@
 	ip = op = buf;
 	while (count-- > 0) {
 		/* compare with special character */
-		if (*ip == delim) {
+		if (*ip == CASE_DELIMITER) {
 			/* followed by another special character? */
-			if (count && ip[1] == delim) {
+			if (count && ip[1] == CASE_DELIMITER) {
 				/*
 				 * ... then put a single copy of the special
 				 * character to the output string
@@ -550,7 +394,7 @@
 			/* not the special character */
 			if (toggle)
 				/* but case switching is on */
-				if (tolower)
+				if (sclp_tty_tolower)
 					/* switch to uppercase */
 					*op++ = _ebc_toupper[(int) *ip++];
 				else
@@ -570,30 +414,12 @@
 	int count;
 
 	count = end - start;
-	/*
-	 * if set in ioctl convert EBCDIC to lower case
-	 * (modify original input in SCCB)
-	 */
-	if (sclp_ioctls.tolower)
+	if (sclp_tty_tolower)
 		EBC_TOLOWER(start, count);
-
-	/*
-	 * if set in ioctl find out characters in lower or upper case
-	 * (depends on current case) separated by a special character,
-	 * works on EBCDIC
-	 */
-	if (sclp_ioctls.delim)
-		count = sclp_switch_cases(start, count,
-					  sclp_ioctls.delim,
-					  sclp_ioctls.tolower);
-
+	count = sclp_switch_cases(start, count);
 	/* convert EBCDIC to ASCII (modify original input in SCCB) */
 	sclp_ebcasc_str(start, count);
 
-	/* if set in ioctl write operators input to console  */
-	if (sclp_ioctls.echo)
-		sclp_tty_write(sclp_tty, start, count);
-
 	/* transfer input to high level driver */
 	sclp_tty_input(start, count);
 }
@@ -717,7 +543,6 @@
 	.write_room = sclp_tty_write_room,
 	.chars_in_buffer = sclp_tty_chars_in_buffer,
 	.flush_buffer = sclp_tty_flush_buffer,
-	.ioctl = sclp_tty_ioctl,
 };
 
 static int __init
@@ -736,9 +561,6 @@
 
 	rc = sclp_rw_init();
 	if (rc) {
-		printk(KERN_ERR SCLP_TTY_PRINT_HEADER
-		       "could not register tty - "
-		       "sclp_rw_init returned %d\n", rc);
 		put_tty_driver(driver);
 		return rc;
 	}
@@ -754,7 +576,6 @@
 	}
 	INIT_LIST_HEAD(&sclp_tty_outqueue);
 	spin_lock_init(&sclp_tty_lock);
-	init_waitqueue_head(&sclp_tty_waitq);
 	init_timer(&sclp_tty_timer);
 	sclp_ttybuf = NULL;
 	sclp_tty_buffer_count = 0;
@@ -763,11 +584,10 @@
 		 * save 4 characters for the CPU number
 		 * written at start of each line by VM/CP
 		 */
-		sclp_ioctls_init.columns = 76;
+		sclp_tty_columns = 76;
 		/* case input lines to lowercase */
-		sclp_ioctls_init.tolower = 1;
+		sclp_tty_tolower = 1;
 	}
-	sclp_ioctls = sclp_ioctls_init;
 	sclp_tty_chars_count = 0;
 	sclp_tty = NULL;
 
@@ -792,9 +612,6 @@
 	tty_set_operations(driver, &sclp_ops);
 	rc = tty_register_driver(driver);
 	if (rc) {
-		printk(KERN_ERR SCLP_TTY_PRINT_HEADER
-		       "could not register tty - "
-		       "tty_register_driver returned %d\n", rc);
 		put_tty_driver(driver);
 		return rc;
 	}
diff --git a/drivers/s390/char/sclp_tty.h b/drivers/s390/char/sclp_tty.h
index 0ce2c1f..4b965b2 100644
--- a/drivers/s390/char/sclp_tty.h
+++ b/drivers/s390/char/sclp_tty.h
@@ -11,61 +11,8 @@
 #ifndef __SCLP_TTY_H__
 #define __SCLP_TTY_H__
 
-#include <linux/ioctl.h>
-#include <linux/termios.h>
 #include <linux/tty_driver.h>
 
-/* This is the type of data structures storing sclp ioctl setting. */
-struct sclp_ioctls {
-	unsigned short htab;
-	unsigned char echo;
-	unsigned short columns;
-	unsigned char final_nl;
-	unsigned short max_sccb;
-	unsigned short kmem_sccb;	/* can't be modified at run time */
-	unsigned char tolower;
-	unsigned char delim;
-};
-
-/* must be unique, FIXME: must be added in Documentation/ioctl_number.txt */
-#define SCLP_IOCTL_LETTER 'B'
-
-/* set width of horizontal tabulator */
-#define TIOCSCLPSHTAB	_IOW(SCLP_IOCTL_LETTER, 0, unsigned short)
-/* enable/disable echo of input (independent from line discipline) */
-#define TIOCSCLPSECHO	_IOW(SCLP_IOCTL_LETTER, 1, unsigned char)
-/* set number of colums for output */
-#define TIOCSCLPSCOLS	_IOW(SCLP_IOCTL_LETTER, 2, unsigned short)
-/* enable/disable writing without final new line character */
-#define TIOCSCLPSNL	_IOW(SCLP_IOCTL_LETTER, 4, signed char)
-/* set the maximum buffers size for output, rounded up to next 4kB boundary */
-#define TIOCSCLPSOBUF	_IOW(SCLP_IOCTL_LETTER, 5, unsigned short)
-/* set initial (default) sclp ioctls */
-#define TIOCSCLPSINIT	_IO(SCLP_IOCTL_LETTER, 6)
-/* enable/disable conversion from upper to lower case of input */
-#define TIOCSCLPSCASE	_IOW(SCLP_IOCTL_LETTER, 7, unsigned char)
-/* set special character used for separating upper and lower case, */
-/* 0x00 disables this feature */
-#define TIOCSCLPSDELIM	_IOW(SCLP_IOCTL_LETTER, 9, unsigned char)
-
-/* get width of horizontal tabulator */
-#define TIOCSCLPGHTAB	_IOR(SCLP_IOCTL_LETTER, 10, unsigned short)
-/* Is echo of input enabled ? (independent from line discipline) */
-#define TIOCSCLPGECHO	_IOR(SCLP_IOCTL_LETTER, 11, unsigned char)
-/* get number of colums for output */
-#define TIOCSCLPGCOLS	_IOR(SCLP_IOCTL_LETTER, 12, unsigned short)
-/* Is writing without final new line character enabled ? */
-#define TIOCSCLPGNL	_IOR(SCLP_IOCTL_LETTER, 14, signed char)
-/* get the maximum buffers size for output */
-#define TIOCSCLPGOBUF	_IOR(SCLP_IOCTL_LETTER, 15, unsigned short)
-/* Is conversion from upper to lower case of input enabled ? */
-#define TIOCSCLPGCASE	_IOR(SCLP_IOCTL_LETTER, 17, unsigned char)
-/* get special character used for separating upper and lower case, */
-/* 0x00 disables this feature */
-#define TIOCSCLPGDELIM	_IOR(SCLP_IOCTL_LETTER, 19, unsigned char)
-/* get the number of buffers/pages got from kernel at startup */
-#define TIOCSCLPGKBUF	_IOR(SCLP_IOCTL_LETTER, 20, unsigned short)
-
 extern struct tty_driver *sclp_tty_driver;
 
 #endif	/* __SCLP_TTY_H__ */
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 3e577f6..ad51738 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -27,7 +27,6 @@
 #include <asm/uaccess.h>
 #include "sclp.h"
 
-#define SCLP_VT220_PRINT_HEADER 	"sclp vt220 tty driver: "
 #define SCLP_VT220_MAJOR		TTY_MAJOR
 #define SCLP_VT220_MINOR		65
 #define SCLP_VT220_DRIVER_NAME		"sclp_vt220"
@@ -82,8 +81,8 @@
 /* Number of characters in current request buffer */
 static int sclp_vt220_buffered_chars;
 
-/* Flag indicating whether this driver has already been initialized */
-static int sclp_vt220_initialized = 0;
+/* Counter controlling core driver initialization. */
+static int __initdata sclp_vt220_init_count;
 
 /* Flag indicating that sclp_vt220_current_request should really
  * have been already queued but wasn't because the SCLP was processing
@@ -609,10 +608,8 @@
 	sclp_vt220_emit_current();
 }
 
-/*
- * Initialize all relevant components and register driver with system.
- */
-static void __init __sclp_vt220_cleanup(void)
+/* Release allocated pages. */
+static void __init __sclp_vt220_free_pages(void)
 {
 	struct list_head *page, *p;
 
@@ -623,21 +620,30 @@
 		else
 			free_bootmem((unsigned long) page, PAGE_SIZE);
 	}
-	if (!list_empty(&sclp_vt220_register.list))
-		sclp_unregister(&sclp_vt220_register);
-	sclp_vt220_initialized = 0;
 }
 
-static int __init __sclp_vt220_init(void)
+/* Release memory and unregister from sclp core. Controlled by init counting -
+ * only the last invoker will actually perform these actions. */
+static void __init __sclp_vt220_cleanup(void)
+{
+	sclp_vt220_init_count--;
+	if (sclp_vt220_init_count != 0)
+		return;
+	sclp_unregister(&sclp_vt220_register);
+	__sclp_vt220_free_pages();
+}
+
+/* Allocate buffer pages and register with sclp core. Controlled by init
+ * counting - only the first invoker will actually perform these actions. */
+static int __init __sclp_vt220_init(int num_pages)
 {
 	void *page;
 	int i;
-	int num_pages;
 	int rc;
 
-	if (sclp_vt220_initialized)
+	sclp_vt220_init_count++;
+	if (sclp_vt220_init_count != 1)
 		return 0;
-	sclp_vt220_initialized = 1;
 	spin_lock_init(&sclp_vt220_lock);
 	INIT_LIST_HEAD(&sclp_vt220_empty);
 	INIT_LIST_HEAD(&sclp_vt220_outqueue);
@@ -649,24 +655,22 @@
 	sclp_vt220_flush_later = 0;
 
 	/* Allocate pages for output buffering */
-	num_pages = slab_is_available() ? MAX_KMEM_PAGES : MAX_CONSOLE_PAGES;
 	for (i = 0; i < num_pages; i++) {
 		if (slab_is_available())
 			page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 		else
 			page = alloc_bootmem_low_pages(PAGE_SIZE);
 		if (!page) {
-			__sclp_vt220_cleanup();
-			return -ENOMEM;
+			rc = -ENOMEM;
+			goto out;
 		}
 		list_add_tail((struct list_head *) page, &sclp_vt220_empty);
 	}
 	rc = sclp_register(&sclp_vt220_register);
+out:
 	if (rc) {
-		printk(KERN_ERR SCLP_VT220_PRINT_HEADER
-		       "could not register vt220 - "
-		       "sclp_register returned %d\n", rc);
-		__sclp_vt220_cleanup();
+		__sclp_vt220_free_pages();
+		sclp_vt220_init_count--;
 	}
 	return rc;
 }
@@ -689,15 +693,13 @@
 {
 	struct tty_driver *driver;
 	int rc;
-	int cleanup;
 
 	/* Note: we're not testing for CONSOLE_IS_SCLP here to preserve
 	 * symmetry between VM and LPAR systems regarding ttyS1. */
 	driver = alloc_tty_driver(1);
 	if (!driver)
 		return -ENOMEM;
-	cleanup = !sclp_vt220_initialized;
-	rc = __sclp_vt220_init();
+	rc = __sclp_vt220_init(MAX_KMEM_PAGES);
 	if (rc)
 		goto out_driver;
 
@@ -713,18 +715,13 @@
 	tty_set_operations(driver, &sclp_vt220_ops);
 
 	rc = tty_register_driver(driver);
-	if (rc) {
-		printk(KERN_ERR SCLP_VT220_PRINT_HEADER
-		       "could not register tty - "
-		       "tty_register_driver returned %d\n", rc);
+	if (rc)
 		goto out_init;
-	}
 	sclp_vt220_driver = driver;
 	return 0;
 
 out_init:
-	if (cleanup)
-		__sclp_vt220_cleanup();
+	__sclp_vt220_cleanup();
 out_driver:
 	put_tty_driver(driver);
 	return rc;
@@ -773,10 +770,9 @@
 {
 	int rc;
 
-	INIT_LIST_HEAD(&sclp_vt220_register.list);
 	if (!CONSOLE_IS_SCLP)
 		return 0;
-	rc = __sclp_vt220_init();
+	rc = __sclp_vt220_init(MAX_CONSOLE_PAGES);
 	if (rc)
 		return rc;
 	/* Attach linux console */
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 874adf3..22ca343 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -196,7 +196,7 @@
 static int
 tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb)
 {
-	if (irb->scsw.dstat == 0x85 /* READY */) {
+	if (irb->scsw.cmd.dstat == 0x85) { /* READY */
 		/* A medium was inserted in the drive. */
 		DBF_EVENT(6, "xuud med\n");
 		tape_34xx_delete_sbid_from(device, 0);
@@ -844,22 +844,22 @@
 	if (request == NULL)
 		return tape_34xx_unsolicited_irq(device, irb);
 
-	if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) &&
-	    (irb->scsw.dstat & DEV_STAT_DEV_END) &&
+	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
+	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
 	    (request->op == TO_WRI)) {
 		/* Write at end of volume */
 		PRINT_INFO("End of volume\n"); /* XXX */
 		return tape_34xx_erp_failed(request, -ENOSPC);
 	}
 
-	if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 		return tape_34xx_unit_check(device, request, irb);
 
-	if (irb->scsw.dstat & DEV_STAT_DEV_END) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
 		/*
 		 * A unit exception occurs on skipping over a tapemark block.
 		 */
-		if (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
+		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
 			if (request->op == TO_BSB || request->op == TO_FSB)
 				request->rescnt++;
 			else
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index 42ce791..8399876 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -837,13 +837,13 @@
 static int
 tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb)
 {
-	if (irb->scsw.dstat == DEV_STAT_CHN_END)
+	if (irb->scsw.cmd.dstat == DEV_STAT_CHN_END)
 		/* Probably result of halt ssch */
 		return TAPE_IO_PENDING;
-	else if (irb->scsw.dstat == 0x85)
+	else if (irb->scsw.cmd.dstat == 0x85)
 		/* Device Ready */
 		DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id);
-	else if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+	else if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 		tape_3590_schedule_work(device, TO_READ_ATTMSG);
 	} else {
 		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
@@ -1515,18 +1515,19 @@
 	if (request == NULL)
 		return tape_3590_unsolicited_irq(device, irb);
 
-	if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) &&
-	    (irb->scsw.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) {
+	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
+	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
+	    (request->op == TO_WRI)) {
 		/* Write at end of volume */
 		DBF_EVENT(2, "End of volume\n");
 		return tape_3590_erp_failed(device, request, irb, -ENOSPC);
 	}
 
-	if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 		return tape_3590_unit_check(device, request, irb);
 
-	if (irb->scsw.dstat & DEV_STAT_DEV_END) {
-		if (irb->scsw.dstat == DEV_STAT_UNIT_EXCEP) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
+		if (irb->scsw.cmd.dstat == DEV_STAT_UNIT_EXCEP) {
 			if (request->op == TO_FSB || request->op == TO_BSB)
 				request->rescnt++;
 			else
@@ -1536,12 +1537,12 @@
 		return tape_3590_done(device, request);
 	}
 
-	if (irb->scsw.dstat & DEV_STAT_CHN_END) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) {
 		DBF_EVENT(2, "cannel end\n");
 		return TAPE_IO_PENDING;
 	}
 
-	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 		DBF_EVENT(2, "Unit Attention when busy..\n");
 		return TAPE_IO_PENDING;
 	}
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index c20e3c5..181a544 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -839,7 +839,7 @@
 
 	PRINT_INFO("-------------------------------------------------\n");
 	PRINT_INFO("DSTAT : %02x  CSTAT: %02x	CPA: %04x\n",
-		   irb->scsw.dstat, irb->scsw.cstat, irb->scsw.cpa);
+		   irb->scsw.cmd.dstat, irb->scsw.cmd.cstat, irb->scsw.cmd.cpa);
 	PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id);
 	if (request != NULL)
 		PRINT_INFO("OP	  : %s\n", tape_op_verbose[request->op]);
@@ -867,7 +867,7 @@
 	else
 		op = "---";
 	DBF_EVENT(3, "DSTAT : %02x   CSTAT: %02x\n",
-		  irb->scsw.dstat,irb->scsw.cstat);
+		  irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
 	DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op);
 	sptr = (unsigned int *) irb->ecw;
 	DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]);
@@ -1083,10 +1083,11 @@
 	 * error might still apply. So we just schedule the request to be
 	 * started later.
 	 */
-	if (irb->scsw.cc != 0 && (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
+	if (irb->scsw.cmd.cc != 0 &&
+	    (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
 	    (request->status == TAPE_REQUEST_IN_IO)) {
 		DBF_EVENT(3,"(%08x): deferred cc=%i, fctl=%i. restarting\n",
-			device->cdev_id, irb->scsw.cc, irb->scsw.fctl);
+			device->cdev_id, irb->scsw.cmd.cc, irb->scsw.cmd.fctl);
 		request->status = TAPE_REQUEST_QUEUED;
 		schedule_delayed_work(&device->tape_dnr, HZ);
 		return;
@@ -1094,8 +1095,8 @@
 
 	/* May be an unsolicited irq */
 	if(request != NULL)
-		request->rescnt = irb->scsw.count;
-	else if ((irb->scsw.dstat == 0x85 || irb->scsw.dstat == 0x80) &&
+		request->rescnt = irb->scsw.cmd.count;
+	else if ((irb->scsw.cmd.dstat == 0x85 || irb->scsw.cmd.dstat == 0x80) &&
 		 !list_empty(&device->req_queue)) {
 		/* Not Ready to Ready after long busy ? */
 		struct tape_request *req;
@@ -1111,7 +1112,7 @@
 			return;
 		}
 	}
-	if (irb->scsw.dstat != 0x0c) {
+	if (irb->scsw.cmd.dstat != 0x0c) {
 		/* Set the 'ONLINE' flag depending on sense byte 1 */
 		if(*(((__u8 *) irb->ecw) + 1) & SENSE_DRIVE_ONLINE)
 			device->tape_generic_status |= GMT_ONLINE(~0);
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 5043150..a7fe630 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -663,7 +663,7 @@
 tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
 {
 	/* Handle ATTN. Schedule tasklet to read aid. */
-	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 		if (!tp->throttle)
 			tty3270_issue_read(tp, 0);
 		else
@@ -671,11 +671,11 @@
 	}
 
 	if (rq) {
-		if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
 			rq->rc = -EIO;
 		else
 			/* Normal end. Copy residual count. */
-			rq->rescnt = irb->scsw.count;
+			rq->rescnt = irb->scsw.cmd.count;
 	}
 	return RAW3270_IO_DONE;
 }
@@ -1792,15 +1792,12 @@
 	tty_set_operations(driver, &tty3270_ops);
 	ret = tty_register_driver(driver);
 	if (ret) {
-		printk(KERN_ERR "tty3270 registration failed with %d\n", ret);
 		put_tty_driver(driver);
 		return ret;
 	}
 	tty3270_driver = driver;
 	ret = raw3270_register_notifier(tty3270_notifier);
 	if (ret) {
-		printk(KERN_ERR "tty3270 notifier registration failed "
-		       "with %d\n", ret);
 		put_tty_driver(driver);
 		return ret;
 
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index 2f419b0..401ea84 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -61,30 +61,24 @@
 static ssize_t
 vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos)
 {
-	size_t tocopy;
+	ssize_t ret;
+	size_t size;
 	struct vmcp_session *session;
 
-	session = (struct vmcp_session *)file->private_data;
+	session = file->private_data;
 	if (mutex_lock_interruptible(&session->mutex))
 		return -ERESTARTSYS;
 	if (!session->response) {
 		mutex_unlock(&session->mutex);
 		return 0;
 	}
-	if (*ppos > session->resp_size) {
-		mutex_unlock(&session->mutex);
-		return 0;
-	}
-	tocopy = min(session->resp_size - (size_t) (*ppos), count);
-	tocopy = min(tocopy, session->bufsize - (size_t) (*ppos));
+	size = min_t(size_t, session->resp_size, session->bufsize);
+	ret = simple_read_from_buffer(buff, count, ppos,
+					session->response, size);
 
-	if (copy_to_user(buff, session->response + (*ppos), tocopy)) {
-		mutex_unlock(&session->mutex);
-		return -EFAULT;
-	}
 	mutex_unlock(&session->mutex);
-	*ppos += tocopy;
-	return tocopy;
+
+	return ret;
 }
 
 static ssize_t
@@ -198,27 +192,23 @@
 		PRINT_WARN("z/VM CP interface is only available under z/VM\n");
 		return -ENODEV;
 	}
+
 	vmcp_debug = debug_register("vmcp", 1, 1, 240);
-	if (!vmcp_debug) {
-		PRINT_ERR("z/VM CP interface not loaded. Could not register "
-			   "debug feature\n");
+	if (!vmcp_debug)
 		return -ENOMEM;
-	}
+
 	ret = debug_register_view(vmcp_debug, &debug_hex_ascii_view);
 	if (ret) {
-		PRINT_ERR("z/VM CP interface not loaded. Could not register "
-			  "debug feature view. Error code: %d\n", ret);
 		debug_unregister(vmcp_debug);
 		return ret;
 	}
+
 	ret = misc_register(&vmcp_dev);
 	if (ret) {
-		PRINT_ERR("z/VM CP interface not loaded. Could not register "
-			   "misc device. Error code: %d\n", ret);
 		debug_unregister(vmcp_debug);
 		return ret;
 	}
-	PRINT_INFO("z/VM CP interface loaded\n");
+
 	return 0;
 }
 
@@ -226,7 +216,6 @@
 {
 	misc_deregister(&vmcp_dev);
 	debug_unregister(vmcp_debug);
-	PRINT_INFO("z/VM CP interface unloaded.\n");
 }
 
 module_init(vmcp_init);
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 2c2428c..a246bc7 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -216,9 +216,7 @@
 	char *tail;
 	int len,i;
 
-	printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command);
 	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
-	printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response);
 	len = strnlen(cp_response,sizeof(cp_response));
 	// now the parsing
 	tail=strnchr(cp_response,len,'=');
@@ -268,11 +266,7 @@
 			 logptr->recording_name,
 			 qid_string);
 
-		printk (KERN_DEBUG "vmlogrdr: recording command: %s\n",
-			cp_command);
 		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
-		printk (KERN_DEBUG "vmlogrdr: recording response: %s",
-			cp_response);
 	}
 
 	memset(cp_command, 0x00, sizeof(cp_command));
@@ -282,10 +276,7 @@
 		onoff,
 		qid_string);
 
-	printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
 	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
-	printk (KERN_DEBUG "vmlogrdr: recording response: %s",
-		cp_response);
 	/* The recording command will usually answer with 'Command complete'
 	 * on success, but when the specific service was never connected
 	 * before then there might be an additional informational message
@@ -567,10 +558,7 @@
 			 "RECORDING %s PURGE ",
 			 priv->recording_name);
 
-	printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
 	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
-	printk (KERN_DEBUG "vmlogrdr: recording response: %s",
-		cp_response);
 
 	return count;
 }
@@ -682,28 +670,20 @@
 
 	/* Register with iucv driver */
 	ret = iucv_register(&vmlogrdr_iucv_handler, 1);
-	if (ret) {
-		printk (KERN_ERR "vmlogrdr: failed to register with "
-			"iucv driver\n");
+	if (ret)
 		goto out;
-	}
 
 	ret = driver_register(&vmlogrdr_driver);
-	if (ret) {
-		printk(KERN_ERR "vmlogrdr: failed to register driver.\n");
+	if (ret)
 		goto out_iucv;
-	}
 
 	ret = driver_create_file(&vmlogrdr_driver,
 				 &driver_attr_recording_status);
-	if (ret) {
-		printk(KERN_ERR "vmlogrdr: failed to add driver attribute.\n");
+	if (ret)
 		goto out_driver;
-	}
 
 	vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");
 	if (IS_ERR(vmlogrdr_class)) {
-		printk(KERN_ERR "vmlogrdr: failed to create class.\n");
 		ret = PTR_ERR(vmlogrdr_class);
 		vmlogrdr_class = NULL;
 		goto out_attr;
@@ -871,12 +851,10 @@
 	rc = vmlogrdr_register_cdev(dev);
 	if (rc)
 		goto cleanup;
-	printk (KERN_INFO "vmlogrdr: driver loaded\n");
 	return 0;
 
 cleanup:
 	vmlogrdr_cleanup();
-	printk (KERN_ERR "vmlogrdr: driver not loaded.\n");
 	return rc;
 }
 
@@ -884,7 +862,6 @@
 static void __exit vmlogrdr_exit(void)
 {
 	vmlogrdr_cleanup();
-	printk (KERN_INFO "vmlogrdr: driver unloaded\n");
 	return;
 }
 
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 83ae9a8..49cba9e 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -277,7 +277,8 @@
 	struct urdev *urd;
 
 	TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
-	      intparm, irb->scsw.cstat, irb->scsw.dstat, irb->scsw.count);
+	      intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+	      irb->scsw.cmd.count);
 
 	if (!intparm) {
 		TRACE("ur_int_handler: unsolicited interrupt\n");
@@ -288,7 +289,7 @@
 	/* On special conditions irb is an error pointer */
 	if (IS_ERR(irb))
 		urd->io_request_rc = PTR_ERR(irb);
-	else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+	else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
 		urd->io_request_rc = 0;
 	else
 		urd->io_request_rc = -EIO;
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index 19f8389..56b3eab 100644
--- a/drivers/s390/char/vmwatchdog.c
+++ b/drivers/s390/char/vmwatchdog.c
@@ -92,23 +92,15 @@
 
 	func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init;
 	ret = __diag288(func, vmwdt_interval, ebc_cmd, len);
+	WARN_ON(ret != 0);
 	kfree(ebc_cmd);
-
-	if (ret) {
-		printk(KERN_WARNING "%s: problem setting interval %d, "
-			"cmd %s\n", __func__, vmwdt_interval,
-			vmwdt_cmd);
-	}
 	return ret;
 }
 
 static int vmwdt_disable(void)
 {
 	int ret = __diag288(wdt_cancel, 0, "", 0);
-	if (ret) {
-		printk(KERN_WARNING "%s: problem disabling watchdog\n",
-			__func__);
-	}
+	WARN_ON(ret != 0);
 	return ret;
 }
 
@@ -121,10 +113,8 @@
 	static char __initdata ebc_begin[] = {
 		194, 197, 199, 201, 213
 	};
-	if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) {
-		printk(KERN_INFO "z/VM watchdog not available\n");
+	if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0)
 		return -EINVAL;
-	}
 	return vmwdt_disable();
 }
 
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index bbbd14e..047dd92 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -223,12 +223,10 @@
 	/* get info for boot cpu from lowcore, stored in the HSA */
 
 	sa = kmalloc(sizeof(*sa), GFP_KERNEL);
-	if (!sa) {
-		ERROR_MSG("kmalloc failed: %s: %i\n",__func__, __LINE__);
+	if (!sa)
 		return -ENOMEM;
-	}
 	if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) {
-		ERROR_MSG("could not copy from HSA\n");
+		TRACE("could not copy from HSA\n");
 		kfree(sa);
 		return -EIO;
 	}
@@ -511,6 +509,8 @@
  */
 static int __init sys_info_init(enum arch_id arch)
 {
+	int rc;
+
 	switch (arch) {
 	case ARCH_S390X:
 		MSG("DETECTED 'S390X (64 bit) OS'\n");
@@ -529,10 +529,9 @@
 		return -EINVAL;
 	}
 	sys_info.arch = arch;
-	if (init_cpu_info(arch)) {
-		ERROR_MSG("get cpu info failed\n");
-		return -ENOMEM;
-	}
+	rc = init_cpu_info(arch);
+	if (rc)
+		return rc;
 	sys_info.mem_size = real_memory_size;
 
 	return 0;
@@ -544,12 +543,12 @@
 
 	rc = sclp_sdias_blk_count();
 	if (rc < 0) {
-		ERROR_MSG("Could not determine HSA size\n");
+		TRACE("Could not determine HSA size\n");
 		return rc;
 	}
 	act_hsa_size = (rc - 1) * PAGE_SIZE;
 	if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
-		ERROR_MSG("HSA size too small: %i\n", act_hsa_size);
+		TRACE("HSA size too small: %i\n", act_hsa_size);
 		return -EINVAL;
 	}
 	return 0;
@@ -590,16 +589,12 @@
 		goto fail;
 
 	rc = check_sdias();
-	if (rc) {
-		ERROR_MSG("Dump initialization failed\n");
+	if (rc)
 		goto fail;
-	}
 
 	rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
-	if (rc) {
-		ERROR_MSG("sdial memcpy for arch id failed\n");
+	if (rc)
 		goto fail;
-	}
 
 #ifndef __s390x__
 	if (arch == ARCH_S390X) {
@@ -610,10 +605,8 @@
 #endif
 
 	rc = sys_info_init(arch);
-	if (rc) {
-		ERROR_MSG("arch init failed\n");
+	if (rc)
 		goto fail;
-	}
 
 	zcore_header_init(arch, &zcore_header);
 
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index cfaf77b..91e9e3f 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,9 +2,11 @@
 # Makefile for the S/390 common i/o drivers
 #
 
-obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o
+obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o scsw.o \
+	fcx.o itcw.o
 ccw_device-objs += device.o device_fsm.o device_ops.o
 ccw_device-objs += device_id.o device_pgid.o device_status.o
 obj-y += ccw_device.o cmf.o
+obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
 obj-$(CONFIG_CCWGROUP) += ccwgroup.o
 obj-$(CONFIG_QDIO) += qdio.o
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index b7a07a8..fe6cea1 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -15,6 +15,7 @@
 #include <linux/rcupdate.h>
 
 #include <asm/airq.h>
+#include <asm/isc.h>
 
 #include "cio.h"
 #include "cio_debug.h"
@@ -33,15 +34,15 @@
 	void *drv_data;
 };
 
-static union indicator_t indicators;
-static struct airq_t *airqs[NR_AIRQS];
+static union indicator_t indicators[MAX_ISC];
+static struct airq_t *airqs[MAX_ISC][NR_AIRQS];
 
-static int register_airq(struct airq_t *airq)
+static int register_airq(struct airq_t *airq, u8 isc)
 {
 	int i;
 
 	for (i = 0; i < NR_AIRQS; i++)
-		if (!cmpxchg(&airqs[i], NULL, airq))
+		if (!cmpxchg(&airqs[isc][i], NULL, airq))
 			return i;
 	return -ENOMEM;
 }
@@ -50,18 +51,21 @@
  * s390_register_adapter_interrupt() - register adapter interrupt handler
  * @handler: adapter handler to be registered
  * @drv_data: driver data passed with each call to the handler
+ * @isc: isc for which the handler should be called
  *
  * Returns:
  *  Pointer to the indicator to be used on success
  *  ERR_PTR() if registration failed
  */
 void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
-				      void *drv_data)
+				      void *drv_data, u8 isc)
 {
 	struct airq_t *airq;
 	char dbf_txt[16];
 	int ret;
 
+	if (isc > MAX_ISC)
+		return ERR_PTR(-EINVAL);
 	airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
 	if (!airq) {
 		ret = -ENOMEM;
@@ -69,34 +73,35 @@
 	}
 	airq->handler = handler;
 	airq->drv_data = drv_data;
-	ret = register_airq(airq);
-	if (ret < 0)
-		kfree(airq);
+
+	ret = register_airq(airq, isc);
 out:
 	snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
 	CIO_TRACE_EVENT(4, dbf_txt);
-	if (ret < 0)
+	if (ret < 0) {
+		kfree(airq);
 		return ERR_PTR(ret);
-	else
-		return &indicators.byte[ret];
+	} else
+		return &indicators[isc].byte[ret];
 }
 EXPORT_SYMBOL(s390_register_adapter_interrupt);
 
 /**
  * s390_unregister_adapter_interrupt - unregister adapter interrupt handler
  * @ind: indicator for which the handler is to be unregistered
+ * @isc: interruption subclass
  */
-void s390_unregister_adapter_interrupt(void *ind)
+void s390_unregister_adapter_interrupt(void *ind, u8 isc)
 {
 	struct airq_t *airq;
 	char dbf_txt[16];
 	int i;
 
-	i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]);
+	i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]);
 	snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
 	CIO_TRACE_EVENT(4, dbf_txt);
-	indicators.byte[i] = 0;
-	airq = xchg(&airqs[i], NULL);
+	indicators[isc].byte[i] = 0;
+	airq = xchg(&airqs[isc][i], NULL);
 	/*
 	 * Allow interrupts to complete. This will ensure that the airq handle
 	 * is no longer referenced by any interrupt handler.
@@ -108,7 +113,7 @@
 
 #define INDICATOR_MASK	(0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
 
-void do_adapter_IO(void)
+void do_adapter_IO(u8 isc)
 {
 	int w;
 	int i;
@@ -120,22 +125,22 @@
 	 * fetch operations.
 	 */
 	for (w = 0; w < NR_AIRQ_WORDS; w++) {
-		word = indicators.word[w];
+		word = indicators[isc].word[w];
 		i = w * NR_AIRQS_PER_WORD;
 		/*
 		 * Check bytes within word for active indicators.
 		 */
 		while (word) {
 			if (word & INDICATOR_MASK) {
-				airq = airqs[i];
+				airq = airqs[isc][i];
 				if (likely(airq))
-					airq->handler(&indicators.byte[i],
+					airq->handler(&indicators[isc].byte[i],
 						      airq->drv_data);
 				else
 					/*
 					 * Reset ill-behaved indicator.
 					 */
-					indicators.byte[i] = 0;
+					indicators[isc].byte[i] = 0;
 			}
 			word <<= 8;
 			i++;
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 297cdce..db00b05 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -18,6 +18,7 @@
 #include <asm/chpid.h>
 #include <asm/sclp.h>
 
+#include "../s390mach.h"
 #include "cio.h"
 #include "css.h"
 #include "ioasm.h"
@@ -94,6 +95,7 @@
 	}
 	return opm;
 }
+EXPORT_SYMBOL_GPL(chp_get_sch_opm);
 
 /**
  * chp_is_registered - check if a channel-path is registered
@@ -121,11 +123,8 @@
 	CIO_TRACE_EVENT(2, dbf_text);
 
 	status = chp_get_status(chpid);
-	if (!on && !status) {
-		printk(KERN_ERR "cio: chpid %x.%02x is already offline\n",
-		       chpid.cssid, chpid.id);
-		return -EINVAL;
-	}
+	if (!on && !status)
+		return 0;
 
 	set_chp_logically_online(chpid, on);
 	chsc_chp_vary(chpid, on);
@@ -141,21 +140,14 @@
 {
 	struct channel_path *chp;
 	struct device *device;
-	unsigned int size;
 
 	device = container_of(kobj, struct device, kobj);
 	chp = to_channelpath(device);
 	if (!chp->cmg_chars)
 		return 0;
 
-	size = sizeof(struct cmg_chars);
-
-	if (off > size)
-		return 0;
-	if (off + count > size)
-		count = size - off;
-	memcpy(buf, chp->cmg_chars + off, count);
-	return count;
+	return memory_read_from_buffer(buf, count, &off,
+				chp->cmg_chars, sizeof(struct cmg_chars));
 }
 
 static struct bin_attribute chp_measurement_chars_attr = {
@@ -405,7 +397,7 @@
 		 chpid.id);
 
 	/* Obtain channel path description and fill it in. */
-	ret = chsc_determine_channel_path_description(chpid, &chp->desc);
+	ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc);
 	if (ret)
 		goto out_free;
 	if ((chp->desc.flags & 0x80) == 0) {
@@ -413,8 +405,7 @@
 		goto out_free;
 	}
 	/* Get channel-measurement characteristics. */
-	if (css_characteristics_avail && css_chsc_characteristics.scmc
-	    && css_chsc_characteristics.secm) {
+	if (css_chsc_characteristics.scmc && css_chsc_characteristics.secm) {
 		ret = chsc_get_channel_measurement_chars(chp);
 		if (ret)
 			goto out_free;
@@ -476,26 +467,74 @@
 
 /**
  * chp_process_crw - process channel-path status change
- * @id: channel-path ID number
- * @status: non-zero if channel-path has become available, zero otherwise
+ * @crw0: channel report-word to handler
+ * @crw1: second channel-report word (always NULL)
+ * @overflow: crw overflow indication
  *
  * Handle channel-report-words indicating that the status of a channel-path
  * has changed.
  */
-void chp_process_crw(int id, int status)
+static void chp_process_crw(struct crw *crw0, struct crw *crw1,
+			    int overflow)
 {
 	struct chp_id chpid;
 
+	if (overflow) {
+		css_schedule_eval_all();
+		return;
+	}
+	CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
+		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+		      crw0->erc, crw0->rsid);
+	/*
+	 * Check for solicited machine checks. These are
+	 * created by reset channel path and need not be
+	 * handled here.
+	 */
+	if (crw0->slct) {
+		CIO_CRW_EVENT(2, "solicited machine check for "
+			      "channel path %02X\n", crw0->rsid);
+		return;
+	}
 	chp_id_init(&chpid);
-	chpid.id = id;
-	if (status) {
+	chpid.id = crw0->rsid;
+	switch (crw0->erc) {
+	case CRW_ERC_IPARM: /* Path has come. */
 		if (!chp_is_registered(chpid))
 			chp_new(chpid);
 		chsc_chp_online(chpid);
-	} else
+		break;
+	case CRW_ERC_PERRI: /* Path has gone. */
+	case CRW_ERC_PERRN:
 		chsc_chp_offline(chpid);
+		break;
+	default:
+		CIO_CRW_EVENT(2, "Don't know how to handle erc=%x\n",
+			      crw0->erc);
+	}
 }
 
+int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct chp_link *link)
+{
+	int i;
+	int mask;
+
+	for (i = 0; i < 8; i++) {
+		mask = 0x80 >> i;
+		if (!(ssd->path_mask & mask))
+			continue;
+		if (!chp_id_is_equal(&ssd->chpid[i], &link->chpid))
+			continue;
+		if ((ssd->fla_valid_mask & mask) &&
+		    ((ssd->fla[i] & link->fla_mask) != link->fla))
+			continue;
+		return mask;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(chp_ssd_get_mask);
+
 static inline int info_bit_num(struct chp_id id)
 {
 	return id.id + id.cssid * (__MAX_CHPID + 1);
@@ -575,6 +614,7 @@
 {
 	struct chp_id chpid;
 	enum cfg_task_t t;
+	int rc;
 
 	mutex_lock(&cfg_lock);
 	t = cfg_none;
@@ -589,14 +629,24 @@
 
 	switch (t) {
 	case cfg_configure:
-		sclp_chp_configure(chpid);
-		info_expire();
-		chsc_chp_online(chpid);
+		rc = sclp_chp_configure(chpid);
+		if (rc)
+			CIO_MSG_EVENT(2, "chp: sclp_chp_configure(%x.%02x)="
+				      "%d\n", chpid.cssid, chpid.id, rc);
+		else {
+			info_expire();
+			chsc_chp_online(chpid);
+		}
 		break;
 	case cfg_deconfigure:
-		sclp_chp_deconfigure(chpid);
-		info_expire();
-		chsc_chp_offline(chpid);
+		rc = sclp_chp_deconfigure(chpid);
+		if (rc)
+			CIO_MSG_EVENT(2, "chp: sclp_chp_deconfigure(%x.%02x)="
+				      "%d\n", chpid.cssid, chpid.id, rc);
+		else {
+			info_expire();
+			chsc_chp_offline(chpid);
+		}
 		break;
 	case cfg_none:
 		/* Get updated information after last change. */
@@ -654,10 +704,16 @@
 static int __init chp_init(void)
 {
 	struct chp_id chpid;
+	int ret;
 
+	ret = s390_register_crw_handler(CRW_RSC_CPATH, chp_process_crw);
+	if (ret)
+		return ret;
 	chp_wq = create_singlethread_workqueue("cio_chp");
-	if (!chp_wq)
+	if (!chp_wq) {
+		s390_unregister_crw_handler(CRW_RSC_CPATH);
 		return -ENOMEM;
+	}
 	INIT_WORK(&cfg_work, cfg_func);
 	init_waitqueue_head(&cfg_wait_queue);
 	if (info_update())
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 6528656..26c3d22 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -12,12 +12,24 @@
 #include <linux/device.h>
 #include <asm/chpid.h>
 #include "chsc.h"
+#include "css.h"
 
 #define CHP_STATUS_STANDBY		0
 #define CHP_STATUS_CONFIGURED		1
 #define CHP_STATUS_RESERVED		2
 #define CHP_STATUS_NOT_RECOGNIZED	3
 
+#define CHP_ONLINE 0
+#define CHP_OFFLINE 1
+#define CHP_VARY_ON 2
+#define CHP_VARY_OFF 3
+
+struct chp_link {
+	struct chp_id chpid;
+	u32 fla_mask;
+	u16 fla;
+};
+
 static inline int chp_test_bit(u8 *bitmap, int num)
 {
 	int byte = num >> 3;
@@ -42,12 +54,11 @@
 u8 chp_get_sch_opm(struct subchannel *sch);
 int chp_is_registered(struct chp_id chpid);
 void *chp_get_chp_desc(struct chp_id chpid);
-void chp_process_crw(int id, int available);
 void chp_remove_cmg_attr(struct channel_path *chp);
 int chp_add_cmg_attr(struct channel_path *chp);
 int chp_new(struct chp_id chpid);
 void chp_cfg_schedule(struct chp_id chpid, int configure);
 void chp_cfg_cancel_deconfigure(struct chp_id chpid);
 int chp_info_get_status(struct chp_id chpid);
-
+int chp_ssd_get_mask(struct chsc_ssd_info *, struct chp_link *);
 #endif /* S390_CHP_H */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 5de8690..65264a3 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -2,8 +2,7 @@
  *  drivers/s390/cio/chsc.c
  *   S/390 common I/O routines -- channel subsystem call
  *
- *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
- *			      IBM Corporation
+ *    Copyright IBM Corp. 1999,2008
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
  *		 Cornelia Huck (cornelia.huck@de.ibm.com)
  *		 Arnd Bergmann (arndb@de.ibm.com)
@@ -16,7 +15,9 @@
 
 #include <asm/cio.h>
 #include <asm/chpid.h>
+#include <asm/chsc.h>
 
+#include "../s390mach.h"
 #include "css.h"
 #include "cio.h"
 #include "cio_debug.h"
@@ -127,77 +128,12 @@
 	return ret;
 }
 
-static int check_for_io_on_path(struct subchannel *sch, int mask)
-{
-	int cc;
-
-	cc = stsch(sch->schid, &sch->schib);
-	if (cc)
-		return 0;
-	if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
-		return 1;
-	return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch)
-{
-	if (cio_clear(sch)) {
-		/* Recheck device in case clear failed. */
-		sch->lpm = 0;
-		if (device_trigger_verify(sch) != 0)
-			css_schedule_eval(sch->schid);
-		return;
-	}
-	/* Request retry of internal operation. */
-	device_set_intretry(sch);
-	/* Call handler. */
-	if (sch->driver && sch->driver->termination)
-		sch->driver->termination(sch);
-}
-
 static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
 {
-	int j;
-	int mask;
-	struct chp_id *chpid = data;
-	struct schib schib;
-
-	for (j = 0; j < 8; j++) {
-		mask = 0x80 >> j;
-		if ((sch->schib.pmcw.pim & mask) &&
-		    (sch->schib.pmcw.chpid[j] == chpid->id))
-			break;
-	}
-	if (j >= 8)
-		return 0;
-
 	spin_lock_irq(sch->lock);
-
-	stsch(sch->schid, &schib);
-	if (!css_sch_is_valid(&schib))
-		goto out_unreg;
-	memcpy(&sch->schib, &schib, sizeof(struct schib));
-	/* Check for single path devices. */
-	if (sch->schib.pmcw.pim == 0x80)
-		goto out_unreg;
-
-	if (check_for_io_on_path(sch, mask)) {
-		if (device_is_online(sch))
-			device_kill_io(sch);
-		else {
-			terminate_internal_io(sch);
-			/* Re-start path verification. */
-			if (sch->driver && sch->driver->verify)
-				sch->driver->verify(sch);
-		}
-	} else {
-		/* trigger path verification. */
-		if (sch->driver && sch->driver->verify)
-			sch->driver->verify(sch);
-		else if (sch->lpm == mask)
+	if (sch->driver && sch->driver->chp_event)
+		if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
 			goto out_unreg;
-	}
-
 	spin_unlock_irq(sch->lock);
 	return 0;
 
@@ -211,15 +147,18 @@
 void chsc_chp_offline(struct chp_id chpid)
 {
 	char dbf_txt[15];
+	struct chp_link link;
 
 	sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id);
 	CIO_TRACE_EVENT(2, dbf_txt);
 
 	if (chp_get_status(chpid) <= 0)
 		return;
+	memset(&link, 0, sizeof(struct chp_link));
+	link.chpid = chpid;
 	/* Wait until previous actions have settled. */
 	css_wait_for_slow_path();
-	for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
+	for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link);
 }
 
 static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
@@ -242,67 +181,25 @@
 	return 0;
 }
 
-struct res_acc_data {
-	struct chp_id chpid;
-	u32 fla_mask;
-	u16 fla;
-};
-
-static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
-			      struct res_acc_data *data)
-{
-	int i;
-	int mask;
-
-	for (i = 0; i < 8; i++) {
-		mask = 0x80 >> i;
-		if (!(ssd->path_mask & mask))
-			continue;
-		if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
-			continue;
-		if ((ssd->fla_valid_mask & mask) &&
-		    ((ssd->fla[i] & data->fla_mask) != data->fla))
-			continue;
-		return mask;
-	}
-	return 0;
-}
-
 static int __s390_process_res_acc(struct subchannel *sch, void *data)
 {
-	int chp_mask, old_lpm;
-	struct res_acc_data *res_data = data;
-
 	spin_lock_irq(sch->lock);
-	chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
-	if (chp_mask == 0)
-		goto out;
-	if (stsch(sch->schid, &sch->schib))
-		goto out;
-	old_lpm = sch->lpm;
-	sch->lpm = ((sch->schib.pmcw.pim &
-		     sch->schib.pmcw.pam &
-		     sch->schib.pmcw.pom)
-		    | chp_mask) & sch->opm;
-	if (!old_lpm && sch->lpm)
-		device_trigger_reprobe(sch);
-	else if (sch->driver && sch->driver->verify)
-		sch->driver->verify(sch);
-out:
+	if (sch->driver && sch->driver->chp_event)
+		sch->driver->chp_event(sch, data, CHP_ONLINE);
 	spin_unlock_irq(sch->lock);
 
 	return 0;
 }
 
-static void s390_process_res_acc (struct res_acc_data *res_data)
+static void s390_process_res_acc(struct chp_link *link)
 {
 	char dbf_txt[15];
 
-	sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid,
-		res_data->chpid.id);
+	sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid,
+		link->chpid.id);
 	CIO_TRACE_EVENT( 2, dbf_txt);
-	if (res_data->fla != 0) {
-		sprintf(dbf_txt, "fla%x", res_data->fla);
+	if (link->fla != 0) {
+		sprintf(dbf_txt, "fla%x", link->fla);
 		CIO_TRACE_EVENT( 2, dbf_txt);
 	}
 	/* Wait until previous actions have settled. */
@@ -315,7 +212,7 @@
 	 * will we have to do.
 	 */
 	for_each_subchannel_staged(__s390_process_res_acc,
-				   s390_process_res_acc_new_sch, res_data);
+				   s390_process_res_acc_new_sch, link);
 }
 
 static int
@@ -388,7 +285,7 @@
 
 static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
 {
-	struct res_acc_data res_data;
+	struct chp_link link;
 	struct chp_id chpid;
 	int status;
 
@@ -404,18 +301,18 @@
 		chp_new(chpid);
 	else if (!status)
 		return;
-	memset(&res_data, 0, sizeof(struct res_acc_data));
-	res_data.chpid = chpid;
+	memset(&link, 0, sizeof(struct chp_link));
+	link.chpid = chpid;
 	if ((sei_area->vf & 0xc0) != 0) {
-		res_data.fla = sei_area->fla;
+		link.fla = sei_area->fla;
 		if ((sei_area->vf & 0xc0) == 0xc0)
 			/* full link address */
-			res_data.fla_mask = 0xffff;
+			link.fla_mask = 0xffff;
 		else
 			/* link address */
-			res_data.fla_mask = 0xff00;
+			link.fla_mask = 0xff00;
 	}
-	s390_process_res_acc(&res_data);
+	s390_process_res_acc(&link);
 }
 
 struct chp_config_data {
@@ -480,17 +377,25 @@
 	}
 }
 
-void chsc_process_crw(void)
+static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
 {
 	struct chsc_sei_area *sei_area;
 
+	if (overflow) {
+		css_schedule_eval_all();
+		return;
+	}
+	CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
+		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+		      crw0->erc, crw0->rsid);
 	if (!sei_page)
 		return;
 	/* Access to sei_page is serialized through machine check handler
 	 * thread, so no need for locking. */
 	sei_area = sei_page;
 
-	CIO_TRACE_EVENT( 2, "prcss");
+	CIO_TRACE_EVENT(2, "prcss");
 	do {
 		memset(sei_area, 0, sizeof(*sei_area));
 		sei_area->request.length = 0x0010;
@@ -509,114 +414,36 @@
 	} while (sei_area->flags & 0x80);
 }
 
-static int __chp_add_new_sch(struct subchannel_id schid, void *data)
-{
-	struct schib schib;
-
-	if (stsch_err(schid, &schib))
-		/* We're through */
-		return -ENXIO;
-
-	/* Put it on the slow path. */
-	css_schedule_eval(schid);
-	return 0;
-}
-
-
-static int __chp_add(struct subchannel *sch, void *data)
-{
-	int i, mask;
-	struct chp_id *chpid = data;
-
-	spin_lock_irq(sch->lock);
-	for (i=0; i<8; i++) {
-		mask = 0x80 >> i;
-		if ((sch->schib.pmcw.pim & mask) &&
-		    (sch->schib.pmcw.chpid[i] == chpid->id))
-			break;
-	}
-	if (i==8) {
-		spin_unlock_irq(sch->lock);
-		return 0;
-	}
-	if (stsch(sch->schid, &sch->schib)) {
-		spin_unlock_irq(sch->lock);
-		css_schedule_eval(sch->schid);
-		return 0;
-	}
-	sch->lpm = ((sch->schib.pmcw.pim &
-		     sch->schib.pmcw.pam &
-		     sch->schib.pmcw.pom)
-		    | mask) & sch->opm;
-
-	if (sch->driver && sch->driver->verify)
-		sch->driver->verify(sch);
-
-	spin_unlock_irq(sch->lock);
-
-	return 0;
-}
-
 void chsc_chp_online(struct chp_id chpid)
 {
 	char dbf_txt[15];
+	struct chp_link link;
 
 	sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
 	CIO_TRACE_EVENT(2, dbf_txt);
 
 	if (chp_get_status(chpid) != 0) {
+		memset(&link, 0, sizeof(struct chp_link));
+		link.chpid = chpid;
 		/* Wait until previous actions have settled. */
 		css_wait_for_slow_path();
-		for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
-					   &chpid);
+		for_each_subchannel_staged(__s390_process_res_acc, NULL,
+					   &link);
 	}
 }
 
 static void __s390_subchannel_vary_chpid(struct subchannel *sch,
 					 struct chp_id chpid, int on)
 {
-	int chp, old_lpm;
-	int mask;
 	unsigned long flags;
+	struct chp_link link;
 
+	memset(&link, 0, sizeof(struct chp_link));
+	link.chpid = chpid;
 	spin_lock_irqsave(sch->lock, flags);
-	old_lpm = sch->lpm;
-	for (chp = 0; chp < 8; chp++) {
-		mask = 0x80 >> chp;
-		if (!(sch->ssd_info.path_mask & mask))
-			continue;
-		if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
-			continue;
-
-		if (on) {
-			sch->opm |= mask;
-			sch->lpm |= mask;
-			if (!old_lpm)
-				device_trigger_reprobe(sch);
-			else if (sch->driver && sch->driver->verify)
-				sch->driver->verify(sch);
-			break;
-		}
-		sch->opm &= ~mask;
-		sch->lpm &= ~mask;
-		if (check_for_io_on_path(sch, mask)) {
-			if (device_is_online(sch))
-				/* Path verification is done after killing. */
-				device_kill_io(sch);
-			else {
-				/* Kill and retry internal I/O. */
-				terminate_internal_io(sch);
-				/* Re-start path verification. */
-				if (sch->driver && sch->driver->verify)
-					sch->driver->verify(sch);
-			}
-		} else if (!sch->lpm) {
-			if (device_trigger_verify(sch) != 0)
-				css_schedule_eval(sch->schid);
-		} else if (sch->driver && sch->driver->verify)
-			sch->driver->verify(sch);
-		break;
-	}
+	if (sch->driver && sch->driver->chp_event)
+		sch->driver->chp_event(sch, &link,
+				       on ? CHP_VARY_ON : CHP_VARY_OFF);
 	spin_unlock_irqrestore(sch->lock, flags);
 }
 
@@ -656,6 +483,10 @@
  */
 int chsc_chp_vary(struct chp_id chpid, int on)
 {
+	struct chp_link link;
+
+	memset(&link, 0, sizeof(struct chp_link));
+	link.chpid = chpid;
 	/* Wait until previous actions have settled. */
 	css_wait_for_slow_path();
 	/*
@@ -664,10 +495,10 @@
 
 	if (on)
 		for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
-					   __s390_vary_chpid_on, &chpid);
+					   __s390_vary_chpid_on, &link);
 	else
 		for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
-					   NULL, &chpid);
+					   NULL, &link);
 
 	return 0;
 }
@@ -797,23 +628,33 @@
 	return ret;
 }
 
-int chsc_determine_channel_path_description(struct chp_id chpid,
-					    struct channel_path_desc *desc)
+int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
+				     int c, int m,
+				     struct chsc_response_struct *resp)
 {
 	int ccode, ret;
 
 	struct {
 		struct chsc_header request;
-		u32 : 24;
+		u32 : 2;
+		u32 m : 1;
+		u32 c : 1;
+		u32 fmt : 4;
+		u32 cssid : 8;
+		u32 : 4;
+		u32 rfmt : 4;
 		u32 first_chpid : 8;
 		u32 : 24;
 		u32 last_chpid : 8;
 		u32 zeroes1;
 		struct chsc_header response;
-		u32 zeroes2;
-		struct channel_path_desc desc;
+		u8 data[PAGE_SIZE - 20];
 	} __attribute__ ((packed)) *scpd_area;
 
+	if ((rfmt == 1) && !css_general_characteristics.fcs)
+		return -EINVAL;
+	if ((rfmt == 2) && !css_general_characteristics.cib)
+		return -EINVAL;
 	scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 	if (!scpd_area)
 		return -ENOMEM;
@@ -821,8 +662,13 @@
 	scpd_area->request.length = 0x0010;
 	scpd_area->request.code = 0x0002;
 
+	scpd_area->cssid = chpid.cssid;
 	scpd_area->first_chpid = chpid.id;
 	scpd_area->last_chpid = chpid.id;
+	scpd_area->m = m;
+	scpd_area->c = c;
+	scpd_area->fmt = fmt;
+	scpd_area->rfmt = rfmt;
 
 	ccode = chsc(scpd_area);
 	if (ccode > 0) {
@@ -833,8 +679,7 @@
 	ret = chsc_error_from_response(scpd_area->response.code);
 	if (ret == 0)
 		/* Success. */
-		memcpy(desc, &scpd_area->desc,
-		       sizeof(struct channel_path_desc));
+		memcpy(resp, &scpd_area->response, scpd_area->response.length);
 	else
 		CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n",
 			      scpd_area->response.code);
@@ -842,6 +687,25 @@
 	free_page((unsigned long)scpd_area);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
+
+int chsc_determine_base_channel_path_desc(struct chp_id chpid,
+					  struct channel_path_desc *desc)
+{
+	struct chsc_response_struct *chsc_resp;
+	int ret;
+
+	chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL);
+	if (!chsc_resp)
+		return -ENOMEM;
+	ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp);
+	if (ret)
+		goto out_free;
+	memcpy(desc, &chsc_resp->data, chsc_resp->length);
+out_free:
+	kfree(chsc_resp);
+	return ret;
+}
 
 static void
 chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
@@ -937,15 +801,23 @@
 
 int __init chsc_alloc_sei_area(void)
 {
+	int ret;
+
 	sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
-	if (!sei_page)
+	if (!sei_page) {
 		CIO_MSG_EVENT(0, "Can't allocate page for processing of "
 			      "chsc machine checks!\n");
-	return (sei_page ? 0 : -ENOMEM);
+		return -ENOMEM;
+	}
+	ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw);
+	if (ret)
+		kfree(sei_page);
+	return ret;
 }
 
 void __init chsc_free_sei_area(void)
 {
+	s390_unregister_crw_handler(CRW_RSC_CSS);
 	kfree(sei_page);
 }
 
@@ -1043,3 +915,52 @@
 
 EXPORT_SYMBOL_GPL(css_general_characteristics);
 EXPORT_SYMBOL_GPL(css_chsc_characteristics);
+
+int chsc_sstpc(void *page, unsigned int op, u16 ctrl)
+{
+	struct {
+		struct chsc_header request;
+		unsigned int rsvd0;
+		unsigned int op : 8;
+		unsigned int rsvd1 : 8;
+		unsigned int ctrl : 16;
+		unsigned int rsvd2[5];
+		struct chsc_header response;
+		unsigned int rsvd3[7];
+	} __attribute__ ((packed)) *rr;
+	int rc;
+
+	memset(page, 0, PAGE_SIZE);
+	rr = page;
+	rr->request.length = 0x0020;
+	rr->request.code = 0x0033;
+	rr->op = op;
+	rr->ctrl = ctrl;
+	rc = chsc(rr);
+	if (rc)
+		return -EIO;
+	rc = (rr->response.code == 0x0001) ? 0 : -EIO;
+	return rc;
+}
+
+int chsc_sstpi(void *page, void *result, size_t size)
+{
+	struct {
+		struct chsc_header request;
+		unsigned int rsvd0[3];
+		struct chsc_header response;
+		char data[size];
+	} __attribute__ ((packed)) *rr;
+	int rc;
+
+	memset(page, 0, PAGE_SIZE);
+	rr = page;
+	rr->request.length = 0x0010;
+	rr->request.code = 0x0038;
+	rc = chsc(rr);
+	if (rc)
+		return -EIO;
+	memcpy(result, &rr->data, size);
+	return (rr->response.code == 0x0001) ? 0 : -EIO;
+}
+
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index d1f5db1..fb6c4d6 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -4,7 +4,8 @@
 #include <linux/types.h>
 #include <linux/device.h>
 #include <asm/chpid.h>
-#include "schid.h"
+#include <asm/chsc.h>
+#include <asm/schid.h>
 
 #define CHSC_SDA_OC_MSS   0x2
 
@@ -36,14 +37,15 @@
 
 struct channel_path;
 
-extern void chsc_process_crw(void);
-
 struct css_general_char {
-	u64 : 41;
+	u64 : 12;
+	u32 dynio : 1;	 /* bit 12 */
+	u32 : 28;
 	u32 aif : 1;     /* bit 41 */
 	u32 : 3;
 	u32 mcss : 1;    /* bit 45 */
-	u32 : 2;
+	u32 fcs : 1;	 /* bit 46 */
+	u32 : 1;
 	u32 ext_mb : 1;  /* bit 48 */
 	u32 : 7;
 	u32 aif_tdd : 1; /* bit 56 */
@@ -51,7 +53,11 @@
 	u32 qebsm : 1;   /* bit 58 */
 	u32 : 8;
 	u32 aif_osa : 1; /* bit 67 */
-	u32 : 28;
+	u32 : 14;
+	u32 cib : 1;	 /* bit 82 */
+	u32 : 5;
+	u32 fcx : 1;	 /* bit 88 */
+	u32 : 7;
 }__attribute__((packed));
 
 struct css_chsc_char {
@@ -78,7 +84,6 @@
 extern int chsc_get_ssd_info(struct subchannel_id schid,
 			     struct chsc_ssd_info *ssd);
 extern int chsc_determine_css_characteristics(void);
-extern int css_characteristics_avail;
 extern int chsc_alloc_sei_area(void);
 extern void chsc_free_sei_area(void);
 
@@ -87,8 +92,11 @@
 extern int chsc_secm(struct channel_subsystem *, int);
 
 int chsc_chp_vary(struct chp_id chpid, int on);
-int chsc_determine_channel_path_description(struct chp_id chpid,
-					    struct channel_path_desc *desc);
+int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
+				     int c, int m,
+				     struct chsc_response_struct *resp);
+int chsc_determine_base_channel_path_desc(struct chp_id chpid,
+					  struct channel_path_desc *desc);
 void chsc_chp_online(struct chp_id chpid);
 void chsc_chp_offline(struct chp_id chpid);
 int chsc_get_channel_measurement_chars(struct channel_path *chp);
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
new file mode 100644
index 0000000..91ca87a
--- /dev/null
+++ b/drivers/s390/cio/chsc_sch.c
@@ -0,0 +1,820 @@
+/*
+ * Driver for s390 chsc subchannels
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+
+#include <asm/cio.h>
+#include <asm/chsc.h>
+#include <asm/isc.h>
+
+#include "cio.h"
+#include "cio_debug.h"
+#include "css.h"
+#include "chsc_sch.h"
+#include "ioasm.h"
+
+static debug_info_t *chsc_debug_msg_id;
+static debug_info_t *chsc_debug_log_id;
+
+#define CHSC_MSG(imp, args...) do {					\
+		debug_sprintf_event(chsc_debug_msg_id, imp , ##args);	\
+	} while (0)
+
+#define CHSC_LOG(imp, txt) do {					\
+		debug_text_event(chsc_debug_log_id, imp , txt);	\
+	} while (0)
+
+static void CHSC_LOG_HEX(int level, void *data, int length)
+{
+	while (length > 0) {
+		debug_event(chsc_debug_log_id, level, data, length);
+		length -= chsc_debug_log_id->buf_size;
+		data += chsc_debug_log_id->buf_size;
+	}
+}
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("driver for s390 chsc subchannels");
+MODULE_LICENSE("GPL");
+
+static void chsc_subchannel_irq(struct subchannel *sch)
+{
+	struct chsc_private *private = sch->private;
+	struct chsc_request *request = private->request;
+	struct irb *irb = (struct irb *)__LC_IRB;
+
+	CHSC_LOG(4, "irb");
+	CHSC_LOG_HEX(4, irb, sizeof(*irb));
+	/* Copy irb to provided request and set done. */
+	if (!request) {
+		CHSC_MSG(0, "Interrupt on sch 0.%x.%04x with no request\n",
+			 sch->schid.ssid, sch->schid.sch_no);
+		return;
+	}
+	private->request = NULL;
+	memcpy(&request->irb, irb, sizeof(*irb));
+	stsch(sch->schid, &sch->schib);
+	complete(&request->completion);
+	put_device(&sch->dev);
+}
+
+static int chsc_subchannel_probe(struct subchannel *sch)
+{
+	struct chsc_private *private;
+	int ret;
+
+	CHSC_MSG(6, "Detected chsc subchannel 0.%x.%04x\n",
+		 sch->schid.ssid, sch->schid.sch_no);
+	sch->isc = CHSC_SCH_ISC;
+	private = kzalloc(sizeof(*private), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+	ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
+	if (ret) {
+		CHSC_MSG(0, "Failed to enable 0.%x.%04x: %d\n",
+			 sch->schid.ssid, sch->schid.sch_no, ret);
+		kfree(private);
+	} else {
+		sch->private = private;
+		if (sch->dev.uevent_suppress) {
+			sch->dev.uevent_suppress = 0;
+			kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
+		}
+	}
+	return ret;
+}
+
+static int chsc_subchannel_remove(struct subchannel *sch)
+{
+	struct chsc_private *private;
+
+	cio_disable_subchannel(sch);
+	private = sch->private;
+	sch->private = NULL;
+	if (private->request) {
+		complete(&private->request->completion);
+		put_device(&sch->dev);
+	}
+	kfree(private);
+	return 0;
+}
+
+static void chsc_subchannel_shutdown(struct subchannel *sch)
+{
+	cio_disable_subchannel(sch);
+}
+
+static struct css_device_id chsc_subchannel_ids[] = {
+	{ .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(css, chsc_subchannel_ids);
+
+static struct css_driver chsc_subchannel_driver = {
+	.owner = THIS_MODULE,
+	.subchannel_type = chsc_subchannel_ids,
+	.irq = chsc_subchannel_irq,
+	.probe = chsc_subchannel_probe,
+	.remove = chsc_subchannel_remove,
+	.shutdown = chsc_subchannel_shutdown,
+	.name = "chsc_subchannel",
+};
+
+static int __init chsc_init_dbfs(void)
+{
+	chsc_debug_msg_id = debug_register("chsc_msg", 16, 1,
+					   16 * sizeof(long));
+	if (!chsc_debug_msg_id)
+		goto out;
+	debug_register_view(chsc_debug_msg_id, &debug_sprintf_view);
+	debug_set_level(chsc_debug_msg_id, 2);
+	chsc_debug_log_id = debug_register("chsc_log", 16, 1, 16);
+	if (!chsc_debug_log_id)
+		goto out;
+	debug_register_view(chsc_debug_log_id, &debug_hex_ascii_view);
+	debug_set_level(chsc_debug_log_id, 2);
+	return 0;
+out:
+	if (chsc_debug_msg_id)
+		debug_unregister(chsc_debug_msg_id);
+	return -ENOMEM;
+}
+
+static void chsc_remove_dbfs(void)
+{
+	debug_unregister(chsc_debug_log_id);
+	debug_unregister(chsc_debug_msg_id);
+}
+
+static int __init chsc_init_sch_driver(void)
+{
+	return css_driver_register(&chsc_subchannel_driver);
+}
+
+static void chsc_cleanup_sch_driver(void)
+{
+	css_driver_unregister(&chsc_subchannel_driver);
+}
+
+static DEFINE_SPINLOCK(chsc_lock);
+
+static int chsc_subchannel_match_next_free(struct device *dev, void *data)
+{
+	struct subchannel *sch = to_subchannel(dev);
+
+	return sch->schib.pmcw.ena && !scsw_fctl(&sch->schib.scsw);
+}
+
+static struct subchannel *chsc_get_next_subchannel(struct subchannel *sch)
+{
+	struct device *dev;
+
+	dev = driver_find_device(&chsc_subchannel_driver.drv,
+				 sch ? &sch->dev : NULL, NULL,
+				 chsc_subchannel_match_next_free);
+	return dev ? to_subchannel(dev) : NULL;
+}
+
+/**
+ * chsc_async() - try to start a chsc request asynchronously
+ * @chsc_area: request to be started
+ * @request: request structure to associate
+ *
+ * Tries to start a chsc request on one of the existing chsc subchannels.
+ * Returns:
+ *  %0 if the request was performed synchronously
+ *  %-EINPROGRESS if the request was successfully started
+ *  %-EBUSY if all chsc subchannels are busy
+ *  %-ENODEV if no chsc subchannels are available
+ * Context:
+ *  interrupts disabled, chsc_lock held
+ */
+static int chsc_async(struct chsc_async_area *chsc_area,
+		      struct chsc_request *request)
+{
+	int cc;
+	struct chsc_private *private;
+	struct subchannel *sch = NULL;
+	int ret = -ENODEV;
+	char dbf[10];
+
+	chsc_area->header.key = PAGE_DEFAULT_KEY;
+	while ((sch = chsc_get_next_subchannel(sch))) {
+		spin_lock(sch->lock);
+		private = sch->private;
+		if (private->request) {
+			spin_unlock(sch->lock);
+			ret = -EBUSY;
+			continue;
+		}
+		chsc_area->header.sid = sch->schid;
+		CHSC_LOG(2, "schid");
+		CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid));
+		cc = chsc(chsc_area);
+		sprintf(dbf, "cc:%d", cc);
+		CHSC_LOG(2, dbf);
+		switch (cc) {
+		case 0:
+			ret = 0;
+			break;
+		case 1:
+			sch->schib.scsw.cmd.fctl |= SCSW_FCTL_START_FUNC;
+			ret = -EINPROGRESS;
+			private->request = request;
+			break;
+		case 2:
+			ret = -EBUSY;
+			break;
+		default:
+			ret = -ENODEV;
+		}
+		spin_unlock(sch->lock);
+		CHSC_MSG(2, "chsc on 0.%x.%04x returned cc=%d\n",
+			 sch->schid.ssid, sch->schid.sch_no, cc);
+		if (ret == -EINPROGRESS)
+			return -EINPROGRESS;
+		put_device(&sch->dev);
+		if (ret == 0)
+			return 0;
+	}
+	return ret;
+}
+
+static void chsc_log_command(struct chsc_async_area *chsc_area)
+{
+	char dbf[10];
+
+	sprintf(dbf, "CHSC:%x", chsc_area->header.code);
+	CHSC_LOG(0, dbf);
+	CHSC_LOG_HEX(0, chsc_area, 32);
+}
+
+static int chsc_examine_irb(struct chsc_request *request)
+{
+	int backed_up;
+
+	if (!scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND)
+		return -EIO;
+	backed_up = scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHAIN_CHECK;
+	request->irb.scsw.cmd.cstat &= ~SCHN_STAT_CHAIN_CHECK;
+	if (scsw_cstat(&request->irb.scsw) == 0)
+		return 0;
+	if (!backed_up)
+		return 0;
+	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROG_CHECK)
+		return -EIO;
+	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROT_CHECK)
+		return -EPERM;
+	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_DATA_CHK)
+		return -EAGAIN;
+	if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_CTRL_CHK)
+		return -EAGAIN;
+	return -EIO;
+}
+
+static int chsc_ioctl_start(void __user *user_area)
+{
+	struct chsc_request *request;
+	struct chsc_async_area *chsc_area;
+	int ret;
+	char dbf[10];
+
+	if (!css_general_characteristics.dynio)
+		/* It makes no sense to try. */
+		return -EOPNOTSUPP;
+	chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+	if (!chsc_area)
+		return -ENOMEM;
+	request = kzalloc(sizeof(*request), GFP_KERNEL);
+	if (!request) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	init_completion(&request->completion);
+	if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	chsc_log_command(chsc_area);
+	spin_lock_irq(&chsc_lock);
+	ret = chsc_async(chsc_area, request);
+	spin_unlock_irq(&chsc_lock);
+	if (ret == -EINPROGRESS) {
+		wait_for_completion(&request->completion);
+		ret = chsc_examine_irb(request);
+	}
+	/* copy area back to user */
+	if (!ret)
+		if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
+			ret = -EFAULT;
+out_free:
+	sprintf(dbf, "ret:%d", ret);
+	CHSC_LOG(0, dbf);
+	kfree(request);
+	free_page((unsigned long)chsc_area);
+	return ret;
+}
+
+static int chsc_ioctl_info_channel_path(void __user *user_cd)
+{
+	struct chsc_chp_cd *cd;
+	int ret, ccode;
+	struct {
+		struct chsc_header request;
+		u32 : 2;
+		u32 m : 1;
+		u32 : 1;
+		u32 fmt1 : 4;
+		u32 cssid : 8;
+		u32 : 8;
+		u32 first_chpid : 8;
+		u32 : 24;
+		u32 last_chpid : 8;
+		u32 : 32;
+		struct chsc_header response;
+		u8 data[PAGE_SIZE - 20];
+	} __attribute__ ((packed)) *scpcd_area;
+
+	scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!scpcd_area)
+		return -ENOMEM;
+	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+	if (!cd) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	if (copy_from_user(cd, user_cd, sizeof(*cd))) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	scpcd_area->request.length = 0x0010;
+	scpcd_area->request.code = 0x0028;
+	scpcd_area->m = cd->m;
+	scpcd_area->fmt1 = cd->fmt;
+	scpcd_area->cssid = cd->chpid.cssid;
+	scpcd_area->first_chpid = cd->chpid.id;
+	scpcd_area->last_chpid = cd->chpid.id;
+
+	ccode = chsc(scpcd_area);
+	if (ccode != 0) {
+		ret = -EIO;
+		goto out_free;
+	}
+	if (scpcd_area->response.code != 0x0001) {
+		ret = -EIO;
+		CHSC_MSG(0, "scpcd: response code=%x\n",
+			 scpcd_area->response.code);
+		goto out_free;
+	}
+	memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length);
+	if (copy_to_user(user_cd, cd, sizeof(*cd)))
+		ret = -EFAULT;
+	else
+		ret = 0;
+out_free:
+	kfree(cd);
+	free_page((unsigned long)scpcd_area);
+	return ret;
+}
+
+static int chsc_ioctl_info_cu(void __user *user_cd)
+{
+	struct chsc_cu_cd *cd;
+	int ret, ccode;
+	struct {
+		struct chsc_header request;
+		u32 : 2;
+		u32 m : 1;
+		u32 : 1;
+		u32 fmt1 : 4;
+		u32 cssid : 8;
+		u32 : 8;
+		u32 first_cun : 8;
+		u32 : 24;
+		u32 last_cun : 8;
+		u32 : 32;
+		struct chsc_header response;
+		u8 data[PAGE_SIZE - 20];
+	} __attribute__ ((packed)) *scucd_area;
+
+	scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!scucd_area)
+		return -ENOMEM;
+	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+	if (!cd) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	if (copy_from_user(cd, user_cd, sizeof(*cd))) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	scucd_area->request.length = 0x0010;
+	scucd_area->request.code = 0x0028;
+	scucd_area->m = cd->m;
+	scucd_area->fmt1 = cd->fmt;
+	scucd_area->cssid = cd->cssid;
+	scucd_area->first_cun = cd->cun;
+	scucd_area->last_cun = cd->cun;
+
+	ccode = chsc(scucd_area);
+	if (ccode != 0) {
+		ret = -EIO;
+		goto out_free;
+	}
+	if (scucd_area->response.code != 0x0001) {
+		ret = -EIO;
+		CHSC_MSG(0, "scucd: response code=%x\n",
+			 scucd_area->response.code);
+		goto out_free;
+	}
+	memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length);
+	if (copy_to_user(user_cd, cd, sizeof(*cd)))
+		ret = -EFAULT;
+	else
+		ret = 0;
+out_free:
+	kfree(cd);
+	free_page((unsigned long)scucd_area);
+	return ret;
+}
+
+static int chsc_ioctl_info_sch_cu(void __user *user_cud)
+{
+	struct chsc_sch_cud *cud;
+	int ret, ccode;
+	struct {
+		struct chsc_header request;
+		u32 : 2;
+		u32 m : 1;
+		u32 : 5;
+		u32 fmt1 : 4;
+		u32 : 2;
+		u32 ssid : 2;
+		u32 first_sch : 16;
+		u32 : 8;
+		u32 cssid : 8;
+		u32 last_sch : 16;
+		u32 : 32;
+		struct chsc_header response;
+		u8 data[PAGE_SIZE - 20];
+	} __attribute__ ((packed)) *sscud_area;
+
+	sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sscud_area)
+		return -ENOMEM;
+	cud = kzalloc(sizeof(*cud), GFP_KERNEL);
+	if (!cud) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	if (copy_from_user(cud, user_cud, sizeof(*cud))) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	sscud_area->request.length = 0x0010;
+	sscud_area->request.code = 0x0006;
+	sscud_area->m = cud->schid.m;
+	sscud_area->fmt1 = cud->fmt;
+	sscud_area->ssid = cud->schid.ssid;
+	sscud_area->first_sch = cud->schid.sch_no;
+	sscud_area->cssid = cud->schid.cssid;
+	sscud_area->last_sch = cud->schid.sch_no;
+
+	ccode = chsc(sscud_area);
+	if (ccode != 0) {
+		ret = -EIO;
+		goto out_free;
+	}
+	if (sscud_area->response.code != 0x0001) {
+		ret = -EIO;
+		CHSC_MSG(0, "sscud: response code=%x\n",
+			 sscud_area->response.code);
+		goto out_free;
+	}
+	memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length);
+	if (copy_to_user(user_cud, cud, sizeof(*cud)))
+		ret = -EFAULT;
+	else
+		ret = 0;
+out_free:
+	kfree(cud);
+	free_page((unsigned long)sscud_area);
+	return ret;
+}
+
+static int chsc_ioctl_conf_info(void __user *user_ci)
+{
+	struct chsc_conf_info *ci;
+	int ret, ccode;
+	struct {
+		struct chsc_header request;
+		u32 : 2;
+		u32 m : 1;
+		u32 : 1;
+		u32 fmt1 : 4;
+		u32 cssid : 8;
+		u32 : 6;
+		u32 ssid : 2;
+		u32 : 8;
+		u64 : 64;
+		struct chsc_header response;
+		u8 data[PAGE_SIZE - 20];
+	} __attribute__ ((packed)) *sci_area;
+
+	sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sci_area)
+		return -ENOMEM;
+	ci = kzalloc(sizeof(*ci), GFP_KERNEL);
+	if (!ci) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	if (copy_from_user(ci, user_ci, sizeof(*ci))) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	sci_area->request.length = 0x0010;
+	sci_area->request.code = 0x0012;
+	sci_area->m = ci->id.m;
+	sci_area->fmt1 = ci->fmt;
+	sci_area->cssid = ci->id.cssid;
+	sci_area->ssid = ci->id.ssid;
+
+	ccode = chsc(sci_area);
+	if (ccode != 0) {
+		ret = -EIO;
+		goto out_free;
+	}
+	if (sci_area->response.code != 0x0001) {
+		ret = -EIO;
+		CHSC_MSG(0, "sci: response code=%x\n",
+			 sci_area->response.code);
+		goto out_free;
+	}
+	memcpy(&ci->scid, &sci_area->response, sci_area->response.length);
+	if (copy_to_user(user_ci, ci, sizeof(*ci)))
+		ret = -EFAULT;
+	else
+		ret = 0;
+out_free:
+	kfree(ci);
+	free_page((unsigned long)sci_area);
+	return ret;
+}
+
+static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
+{
+	struct chsc_comp_list *ccl;
+	int ret, ccode;
+	struct {
+		struct chsc_header request;
+		u32 ctype : 8;
+		u32 : 4;
+		u32 fmt : 4;
+		u32 : 16;
+		u64 : 64;
+		u32 list_parm[2];
+		u64 : 64;
+		struct chsc_header response;
+		u8 data[PAGE_SIZE - 36];
+	} __attribute__ ((packed)) *sccl_area;
+	struct {
+		u32 m : 1;
+		u32 : 31;
+		u32 cssid : 8;
+		u32 : 16;
+		u32 chpid : 8;
+	} __attribute__ ((packed)) *chpid_parm;
+	struct {
+		u32 f_cssid : 8;
+		u32 l_cssid : 8;
+		u32 : 16;
+		u32 res;
+	} __attribute__ ((packed)) *cssids_parm;
+
+	sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sccl_area)
+		return -ENOMEM;
+	ccl = kzalloc(sizeof(*ccl), GFP_KERNEL);
+	if (!ccl) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	sccl_area->request.length = 0x0020;
+	sccl_area->request.code = 0x0030;
+	sccl_area->fmt = ccl->req.fmt;
+	sccl_area->ctype = ccl->req.ctype;
+	switch (sccl_area->ctype) {
+	case CCL_CU_ON_CHP:
+	case CCL_IOP_CHP:
+		chpid_parm = (void *)&sccl_area->list_parm;
+		chpid_parm->m = ccl->req.chpid.m;
+		chpid_parm->cssid = ccl->req.chpid.chp.cssid;
+		chpid_parm->chpid = ccl->req.chpid.chp.id;
+		break;
+	case CCL_CSS_IMG:
+	case CCL_CSS_IMG_CONF_CHAR:
+		cssids_parm = (void *)&sccl_area->list_parm;
+		cssids_parm->f_cssid = ccl->req.cssids.f_cssid;
+		cssids_parm->l_cssid = ccl->req.cssids.l_cssid;
+		break;
+	}
+	ccode = chsc(sccl_area);
+	if (ccode != 0) {
+		ret = -EIO;
+		goto out_free;
+	}
+	if (sccl_area->response.code != 0x0001) {
+		ret = -EIO;
+		CHSC_MSG(0, "sccl: response code=%x\n",
+			 sccl_area->response.code);
+		goto out_free;
+	}
+	memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length);
+	if (copy_to_user(user_ccl, ccl, sizeof(*ccl)))
+		ret = -EFAULT;
+	else
+		ret = 0;
+out_free:
+	kfree(ccl);
+	free_page((unsigned long)sccl_area);
+	return ret;
+}
+
+static int chsc_ioctl_chpd(void __user *user_chpd)
+{
+	struct chsc_cpd_info *chpd;
+	int ret;
+
+	chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
+	if (!chpd)
+		return -ENOMEM;
+	if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
+					       chpd->rfmt, chpd->c, chpd->m,
+					       &chpd->chpdb);
+	if (ret)
+		goto out_free;
+	if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
+		ret = -EFAULT;
+out_free:
+	kfree(chpd);
+	return ret;
+}
+
+static int chsc_ioctl_dcal(void __user *user_dcal)
+{
+	struct chsc_dcal *dcal;
+	int ret, ccode;
+	struct {
+		struct chsc_header request;
+		u32 atype : 8;
+		u32 : 4;
+		u32 fmt : 4;
+		u32 : 16;
+		u32 res0[2];
+		u32 list_parm[2];
+		u32 res1[2];
+		struct chsc_header response;
+		u8 data[PAGE_SIZE - 36];
+	} __attribute__ ((packed)) *sdcal_area;
+
+	sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sdcal_area)
+		return -ENOMEM;
+	dcal = kzalloc(sizeof(*dcal), GFP_KERNEL);
+	if (!dcal) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+	sdcal_area->request.length = 0x0020;
+	sdcal_area->request.code = 0x0034;
+	sdcal_area->atype = dcal->req.atype;
+	sdcal_area->fmt = dcal->req.fmt;
+	memcpy(&sdcal_area->list_parm, &dcal->req.list_parm,
+	       sizeof(sdcal_area->list_parm));
+
+	ccode = chsc(sdcal_area);
+	if (ccode != 0) {
+		ret = -EIO;
+		goto out_free;
+	}
+	if (sdcal_area->response.code != 0x0001) {
+		ret = -EIO;
+		CHSC_MSG(0, "sdcal: response code=%x\n",
+			 sdcal_area->response.code);
+		goto out_free;
+	}
+	memcpy(&dcal->sdcal, &sdcal_area->response,
+	       sdcal_area->response.length);
+	if (copy_to_user(user_dcal, dcal, sizeof(*dcal)))
+		ret = -EFAULT;
+	else
+		ret = 0;
+out_free:
+	kfree(dcal);
+	free_page((unsigned long)sdcal_area);
+	return ret;
+}
+
+static long chsc_ioctl(struct file *filp, unsigned int cmd,
+		       unsigned long arg)
+{
+	CHSC_MSG(2, "chsc_ioctl called, cmd=%x\n", cmd);
+	switch (cmd) {
+	case CHSC_START:
+		return chsc_ioctl_start((void __user *)arg);
+	case CHSC_INFO_CHANNEL_PATH:
+		return chsc_ioctl_info_channel_path((void __user *)arg);
+	case CHSC_INFO_CU:
+		return chsc_ioctl_info_cu((void __user *)arg);
+	case CHSC_INFO_SCH_CU:
+		return chsc_ioctl_info_sch_cu((void __user *)arg);
+	case CHSC_INFO_CI:
+		return chsc_ioctl_conf_info((void __user *)arg);
+	case CHSC_INFO_CCL:
+		return chsc_ioctl_conf_comp_list((void __user *)arg);
+	case CHSC_INFO_CPD:
+		return chsc_ioctl_chpd((void __user *)arg);
+	case CHSC_INFO_DCAL:
+		return chsc_ioctl_dcal((void __user *)arg);
+	default: /* unknown ioctl number */
+		return -ENOIOCTLCMD;
+	}
+}
+
+static const struct file_operations chsc_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = chsc_ioctl,
+	.compat_ioctl = chsc_ioctl,
+};
+
+static struct miscdevice chsc_misc_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "chsc",
+	.fops = &chsc_fops,
+};
+
+static int __init chsc_misc_init(void)
+{
+	return misc_register(&chsc_misc_device);
+}
+
+static void chsc_misc_cleanup(void)
+{
+	misc_deregister(&chsc_misc_device);
+}
+
+static int __init chsc_sch_init(void)
+{
+	int ret;
+
+	ret = chsc_init_dbfs();
+	if (ret)
+		return ret;
+	isc_register(CHSC_SCH_ISC);
+	ret = chsc_init_sch_driver();
+	if (ret)
+		goto out_dbf;
+	ret = chsc_misc_init();
+	if (ret)
+		goto out_driver;
+	return ret;
+out_driver:
+	chsc_cleanup_sch_driver();
+out_dbf:
+	isc_unregister(CHSC_SCH_ISC);
+	chsc_remove_dbfs();
+	return ret;
+}
+
+static void __exit chsc_sch_exit(void)
+{
+	chsc_misc_cleanup();
+	chsc_cleanup_sch_driver();
+	isc_unregister(CHSC_SCH_ISC);
+	chsc_remove_dbfs();
+}
+
+module_init(chsc_sch_init);
+module_exit(chsc_sch_exit);
diff --git a/drivers/s390/cio/chsc_sch.h b/drivers/s390/cio/chsc_sch.h
new file mode 100644
index 0000000..589ebfa
--- /dev/null
+++ b/drivers/s390/cio/chsc_sch.h
@@ -0,0 +1,13 @@
+#ifndef _CHSC_SCH_H
+#define _CHSC_SCH_H
+
+struct chsc_request {
+	struct completion completion;
+	struct irb irb;
+};
+
+struct chsc_private {
+	struct chsc_request *request;
+};
+
+#endif
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index b32d7eb..33bff8f 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -2,7 +2,7 @@
  *  drivers/s390/cio/cio.c
  *   S/390 common I/O routines -- low level i/o calls
  *
- *    Copyright (C) IBM Corp. 1999,2006
+ *    Copyright IBM Corp. 1999,2008
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
  *		 Cornelia Huck (cornelia.huck@de.ibm.com)
  *		 Arnd Bergmann (arndb@de.ibm.com)
@@ -24,7 +24,9 @@
 #include <asm/ipl.h>
 #include <asm/chpid.h>
 #include <asm/airq.h>
+#include <asm/isc.h>
 #include <asm/cpu.h>
+#include <asm/fcx.h>
 #include "cio.h"
 #include "css.h"
 #include "chsc.h"
@@ -72,7 +74,6 @@
 		debug_unregister(cio_debug_trace_id);
 	if (cio_debug_crw_id)
 		debug_unregister(cio_debug_crw_id);
-	printk(KERN_WARNING"cio: could not initialize debugging\n");
 	return -1;
 }
 
@@ -128,7 +129,7 @@
 	local_bh_disable();
 	irq_enter ();
 	spin_lock(sch->lock);
-	memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
+	memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
 	if (sch->driver && sch->driver->irq)
 		sch->driver->irq(sch);
 	spin_unlock(sch->lock);
@@ -167,30 +168,30 @@
 {
 	char dbf_txt[15];
 	int ccode;
-	struct orb *orb;
+	union orb *orb;
 
 	CIO_TRACE_EVENT(4, "stIO");
 	CIO_TRACE_EVENT(4, sch->dev.bus_id);
 
 	orb = &to_io_private(sch)->orb;
 	/* sch is always under 2G. */
-	orb->intparm = (u32)(addr_t)sch;
-	orb->fmt = 1;
+	orb->cmd.intparm = (u32)(addr_t)sch;
+	orb->cmd.fmt = 1;
 
-	orb->pfch = sch->options.prefetch == 0;
-	orb->spnd = sch->options.suspend;
-	orb->ssic = sch->options.suspend && sch->options.inter;
-	orb->lpm = (lpm != 0) ? lpm : sch->lpm;
+	orb->cmd.pfch = sch->options.prefetch == 0;
+	orb->cmd.spnd = sch->options.suspend;
+	orb->cmd.ssic = sch->options.suspend && sch->options.inter;
+	orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm;
 #ifdef CONFIG_64BIT
 	/*
 	 * for 64 bit we always support 64 bit IDAWs with 4k page size only
 	 */
-	orb->c64 = 1;
-	orb->i2k = 0;
+	orb->cmd.c64 = 1;
+	orb->cmd.i2k = 0;
 #endif
-	orb->key = key >> 4;
+	orb->cmd.key = key >> 4;
 	/* issue "Start Subchannel" */
-	orb->cpa = (__u32) __pa(cpa);
+	orb->cmd.cpa = (__u32) __pa(cpa);
 	ccode = ssch(sch->schid, orb);
 
 	/* process condition code */
@@ -202,7 +203,7 @@
 		/*
 		 * initialize device status information
 		 */
-		sch->schib.scsw.actl |= SCSW_ACTL_START_PEND;
+		sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
 		return 0;
 	case 1:		/* status pending */
 	case 2:		/* busy */
@@ -237,7 +238,7 @@
 
 	switch (ccode) {
 	case 0:
-		sch->schib.scsw.actl |= SCSW_ACTL_RESUME_PEND;
+		sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND;
 		return 0;
 	case 1:
 		return -EBUSY;
@@ -277,7 +278,7 @@
 
 	switch (ccode) {
 	case 0:
-		sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND;
+		sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
 		return 0;
 	case 1:		/* status pending */
 	case 2:		/* busy */
@@ -312,7 +313,7 @@
 
 	switch (ccode) {
 	case 0:
-		sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND;
+		sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND;
 		return 0;
 	default:		/* device not operational */
 		return -ENODEV;
@@ -387,8 +388,10 @@
 	return ret;
 }
 
-/*
- * Enable subchannel.
+/**
+ * cio_enable_subchannel - enable a subchannel.
+ * @sch: subchannel to be enabled
+ * @intparm: interruption parameter to set
  */
 int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
 {
@@ -434,12 +437,13 @@
 	CIO_TRACE_EVENT (2, dbf_txt);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(cio_enable_subchannel);
 
-/*
- * Disable subchannel.
+/**
+ * cio_disable_subchannel - disable a subchannel.
+ * @sch: subchannel to disable
  */
-int
-cio_disable_subchannel (struct subchannel *sch)
+int cio_disable_subchannel(struct subchannel *sch)
 {
 	char dbf_txt[15];
 	int ccode;
@@ -455,7 +459,7 @@
 	if (ccode == 3)		/* Not operational. */
 		return -ENODEV;
 
-	if (sch->schib.scsw.actl != 0)
+	if (scsw_actl(&sch->schib.scsw) != 0)
 		/*
 		 * the disable function must not be called while there are
 		 *  requests pending for completion !
@@ -484,6 +488,7 @@
 	CIO_TRACE_EVENT (2, dbf_txt);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(cio_disable_subchannel);
 
 int cio_create_sch_lock(struct subchannel *sch)
 {
@@ -494,27 +499,61 @@
 	return 0;
 }
 
-/*
- * cio_validate_subchannel()
+static int cio_check_devno_blacklisted(struct subchannel *sch)
+{
+	if (is_blacklisted(sch->schid.ssid, sch->schib.pmcw.dev)) {
+		/*
+		 * This device must not be known to Linux. So we simply
+		 * say that there is no device and return ENODEV.
+		 */
+		CIO_MSG_EVENT(6, "Blacklisted device detected "
+			      "at devno %04X, subchannel set %x\n",
+			      sch->schib.pmcw.dev, sch->schid.ssid);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int cio_validate_io_subchannel(struct subchannel *sch)
+{
+	/* Initialization for io subchannels. */
+	if (!css_sch_is_valid(&sch->schib))
+		return -ENODEV;
+
+	/* Devno is valid. */
+	return cio_check_devno_blacklisted(sch);
+}
+
+static int cio_validate_msg_subchannel(struct subchannel *sch)
+{
+	/* Initialization for message subchannels. */
+	if (!css_sch_is_valid(&sch->schib))
+		return -ENODEV;
+
+	/* Devno is valid. */
+	return cio_check_devno_blacklisted(sch);
+}
+
+/**
+ * cio_validate_subchannel - basic validation of subchannel
+ * @sch: subchannel structure to be filled out
+ * @schid: subchannel id
  *
  * Find out subchannel type and initialize struct subchannel.
  * Return codes:
- *   SUBCHANNEL_TYPE_IO for a normal io subchannel
- *   SUBCHANNEL_TYPE_CHSC for a chsc subchannel
- *   SUBCHANNEL_TYPE_MESSAGE for a messaging subchannel
- *   SUBCHANNEL_TYPE_ADM for a adm(?) subchannel
+ *   0 on success
  *   -ENXIO for non-defined subchannels
- *   -ENODEV for subchannels with invalid device number or blacklisted devices
+ *   -ENODEV for invalid subchannels or blacklisted devices
+ *   -EIO for subchannels in an invalid subchannel set
  */
-int
-cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
+int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
 {
 	char dbf_txt[15];
 	int ccode;
 	int err;
 
-	sprintf (dbf_txt, "valsch%x", schid.sch_no);
-	CIO_TRACE_EVENT (4, dbf_txt);
+	sprintf(dbf_txt, "valsch%x", schid.sch_no);
+	CIO_TRACE_EVENT(4, dbf_txt);
 
 	/* Nuke all fields. */
 	memset(sch, 0, sizeof(struct subchannel));
@@ -546,67 +585,21 @@
 	/* Copy subchannel type from path management control word. */
 	sch->st = sch->schib.pmcw.st;
 
-	/*
-	 * ... just being curious we check for non I/O subchannels
-	 */
-	if (sch->st != 0) {
-		CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports "
-			      "non-I/O subchannel type %04X\n",
-			      sch->schid.ssid, sch->schid.sch_no, sch->st);
-		/* We stop here for non-io subchannels. */
-		err = sch->st;
+	switch (sch->st) {
+	case SUBCHANNEL_TYPE_IO:
+		err = cio_validate_io_subchannel(sch);
+		break;
+	case SUBCHANNEL_TYPE_MSG:
+		err = cio_validate_msg_subchannel(sch);
+		break;
+	default:
+		err = 0;
+	}
+	if (err)
 		goto out;
-	}
 
-	/* Initialization for io subchannels. */
-	if (!css_sch_is_valid(&sch->schib)) {
-		err = -ENODEV;
-		goto out;
-	}
-
-	/* Devno is valid. */
-	if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
-		/*
-		 * This device must not be known to Linux. So we simply
-		 * say that there is no device and return ENODEV.
-		 */
-		CIO_MSG_EVENT(6, "Blacklisted device detected "
-			      "at devno %04X, subchannel set %x\n",
-			      sch->schib.pmcw.dev, sch->schid.ssid);
-		err = -ENODEV;
-		goto out;
-	}
-	if (cio_is_console(sch->schid)) {
-		sch->opm = 0xff;
-		sch->isc = 1;
-	} else {
-		sch->opm = chp_get_sch_opm(sch);
-		sch->isc = 3;
-	}
-	sch->lpm = sch->schib.pmcw.pam & sch->opm;
-
-	CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X "
-		      "- PIM = %02X, PAM = %02X, POM = %02X\n",
-		      sch->schib.pmcw.dev, sch->schid.ssid,
-		      sch->schid.sch_no, sch->schib.pmcw.pim,
-		      sch->schib.pmcw.pam, sch->schib.pmcw.pom);
-
-	/*
-	 * We now have to initially ...
-	 *  ... enable "concurrent sense"
-	 *  ... enable "multipath mode" if more than one
-	 *	  CHPID is available. This is done regardless
-	 *	  whether multiple paths are available for us.
-	 */
-	sch->schib.pmcw.csense = 1;	/* concurrent sense */
-	sch->schib.pmcw.ena = 0;
-	if ((sch->lpm & (sch->lpm - 1)) != 0)
-		sch->schib.pmcw.mp = 1;	/* multipath mode */
-	/* clean up possible residual cmf stuff */
-	sch->schib.pmcw.mme = 0;
-	sch->schib.pmcw.mbfc = 0;
-	sch->schib.pmcw.mbi = 0;
-	sch->schib.mba = 0;
+	CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n",
+		      sch->schid.ssid, sch->schid.sch_no, sch->st);
 	return 0;
 out:
 	if (!cio_is_console(schid))
@@ -647,7 +640,7 @@
 		 */
 		if (tpi_info->adapter_IO == 1 &&
 		    tpi_info->int_type == IO_INTERRUPT_TYPE) {
-			do_adapter_IO();
+			do_adapter_IO(tpi_info->isc);
 			continue;
 		}
 		sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
@@ -706,9 +699,9 @@
 	if (!console_subchannel_in_use)
 		return;
 
-	/* disable all but isc 1 (console device) */
+	/* disable all but the console isc */
 	__ctl_store (save_cr6, 6, 6);
-	cr6 = 0x40000000;
+	cr6 = 1UL << (31 - CONSOLE_ISC);
 	__ctl_load (cr6, 6, 6);
 
 	do {
@@ -716,7 +709,7 @@
 		if (!cio_tpi())
 			cpu_relax();
 		spin_lock(console_subchannel.lock);
-	} while (console_subchannel.schib.scsw.actl != 0);
+	} while (console_subchannel.schib.scsw.cmd.actl != 0);
 	/*
 	 * restore previous isc value
 	 */
@@ -761,7 +754,6 @@
 		/* unlike in 2.4, we cannot autoprobe here, since
 		 * the channel subsystem is not fully initialized.
 		 * With some luck, the HWC console can take over */
-		printk(KERN_WARNING "cio: No ccw console found!\n");
 		return -1;
 	}
 	return console_irq;
@@ -778,6 +770,7 @@
 	sch_no = cio_get_console_sch_no();
 	if (sch_no == -1) {
 		console_subchannel_in_use = 0;
+		printk(KERN_WARNING "cio: No ccw console found!\n");
 		return ERR_PTR(-ENODEV);
 	}
 	memset(&console_subchannel, 0, sizeof(struct subchannel));
@@ -790,15 +783,15 @@
 	}
 
 	/*
-	 * enable console I/O-interrupt subclass 1
+	 * enable console I/O-interrupt subclass
 	 */
-	ctl_set_bit(6, 30);
-	console_subchannel.isc = 1;
-	console_subchannel.schib.pmcw.isc = 1;
+	isc_register(CONSOLE_ISC);
+	console_subchannel.schib.pmcw.isc = CONSOLE_ISC;
 	console_subchannel.schib.pmcw.intparm =
 		(u32)(addr_t)&console_subchannel;
 	ret = cio_modify(&console_subchannel);
 	if (ret) {
+		isc_unregister(CONSOLE_ISC);
 		console_subchannel_in_use = 0;
 		return ERR_PTR(ret);
 	}
@@ -810,7 +803,7 @@
 {
 	console_subchannel.schib.pmcw.intparm = 0;
 	cio_modify(&console_subchannel);
-	ctl_clear_bit(6, 24);
+	isc_unregister(CONSOLE_ISC);
 	console_subchannel_in_use = 0;
 }
 
@@ -864,7 +857,7 @@
 }
 
 static int
-__clear_subchannel_easy(struct subchannel_id schid)
+__clear_io_subchannel_easy(struct subchannel_id schid)
 {
 	int retry;
 
@@ -883,6 +876,12 @@
 	return -EBUSY;
 }
 
+static void __clear_chsc_subchannel_easy(void)
+{
+	/* It seems we can only wait for a bit here :/ */
+	udelay_reset(100);
+}
+
 static int pgm_check_occured;
 
 static void cio_reset_pgm_check_handler(void)
@@ -921,11 +920,22 @@
 	case -ENODEV:
 		break;
 	default: /* -EBUSY */
-		if (__clear_subchannel_easy(schid))
-			break; /* give up... */
+		switch (schib.pmcw.st) {
+		case SUBCHANNEL_TYPE_IO:
+			if (__clear_io_subchannel_easy(schid))
+				goto out; /* give up... */
+			break;
+		case SUBCHANNEL_TYPE_CHSC:
+			__clear_chsc_subchannel_easy();
+			break;
+		default:
+			/* No default clear strategy */
+			break;
+		}
 		stsch(schid, &schib);
 		__disable_subchannel_easy(schid, &schib);
 	}
+out:
 	return 0;
 }
 
@@ -1068,3 +1078,61 @@
 	iplinfo->is_qdio = schib.pmcw.qf;
 	return 0;
 }
+
+/**
+ * cio_tm_start_key - perform start function
+ * @sch: subchannel on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ *
+ * Start the tcw on the given subchannel. Return zero on success, non-zero
+ * otherwise.
+ */
+int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key)
+{
+	int cc;
+	union orb *orb = &to_io_private(sch)->orb;
+
+	memset(orb, 0, sizeof(union orb));
+	orb->tm.intparm = (u32) (addr_t) sch;
+	orb->tm.key = key >> 4;
+	orb->tm.b = 1;
+	orb->tm.lpm = lpm ? lpm : sch->lpm;
+	orb->tm.tcw = (u32) (addr_t) tcw;
+	cc = ssch(sch->schid, orb);
+	switch (cc) {
+	case 0:
+		return 0;
+	case 1:
+	case 2:
+		return -EBUSY;
+	default:
+		return cio_start_handle_notoper(sch, lpm);
+	}
+}
+
+/**
+ * cio_tm_intrg - perform interrogate function
+ * @sch - subchannel on which to perform the interrogate function
+ *
+ * If the specified subchannel is running in transport-mode, perform the
+ * interrogate function. Return zero on success, non-zero otherwie.
+ */
+int cio_tm_intrg(struct subchannel *sch)
+{
+	int cc;
+
+	if (!to_io_private(sch)->orb.tm.b)
+		return -EINVAL;
+	cc = xsch(sch->schid);
+	switch (cc) {
+	case 0:
+	case 2:
+		return 0;
+	case 1:
+		return -EBUSY;
+	default:
+		return -ENODEV;
+	}
+}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 6e933ae..3b236d2 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -3,9 +3,12 @@
 
 #include <linux/mutex.h>
 #include <linux/device.h>
+#include <linux/mod_devicetable.h>
 #include <asm/chpid.h>
+#include <asm/cio.h>
+#include <asm/fcx.h>
+#include <asm/schid.h>
 #include "chsc.h"
-#include "schid.h"
 
 /*
  * path management control word
@@ -13,7 +16,7 @@
 struct pmcw {
 	u32 intparm;		/* interruption parameter */
 	u32 qf	 : 1;		/* qdio facility */
-	u32 res0 : 1;		/* reserved zeros */
+	u32 w	 : 1;
 	u32 isc  : 3;		/* interruption sublass */
 	u32 res5 : 3;		/* reserved zeros */
 	u32 ena  : 1;		/* enabled */
@@ -47,7 +50,7 @@
  */
 struct schib {
 	struct pmcw pmcw;	 /* path management control word */
-	struct scsw scsw;	 /* subchannel status word */
+	union scsw scsw;	 /* subchannel status word */
 	__u64 mba;               /* measurement block address */
 	__u8 mda[4];		 /* model dependent area */
 } __attribute__ ((packed,aligned(4)));
@@ -99,8 +102,11 @@
 extern int cio_get_options (struct subchannel *);
 extern int cio_modify (struct subchannel *);
 
+int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
+int cio_tm_intrg(struct subchannel *sch);
+
 int cio_create_sch_lock(struct subchannel *);
-void do_adapter_IO(void);
+void do_adapter_IO(u8 isc);
 void do_IRQ(struct pt_regs *);
 
 /* Use with care. */
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 2808b68..a90b28c 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -341,12 +341,12 @@
 	if (stsch(sch->schid, &sch->schib))
 		return -ENODEV;
 
-	if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) {
+	if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
 		/* Don't copy if a start function is in progress. */
-		if ((!(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED)) &&
-		    (sch->schib.scsw.actl &
+		if ((!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_SUSPENDED)) &&
+		    (scsw_actl(&sch->schib.scsw) &
 		     (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) &&
-		    (!(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)))
+		    (!(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_SEC_STATUS)))
 			return -EBUSY;
 	}
 	cmb_data = cdev->private->cmb;
@@ -612,9 +612,6 @@
 			free_pages((unsigned long)mem, get_order(size));
 		} else if (!mem) {
 			/* no luck */
-			printk(KERN_WARNING "cio: failed to allocate area "
-			       "for measuring %d subchannels\n",
-			       cmb_area.num_channels);
 			ret = -ENOMEM;
 			goto out;
 		} else {
@@ -1230,13 +1227,9 @@
 	switch (val) {
 	case 0:
 		ret = disable_cmf(cdev);
-		if (ret)
-			dev_info(&cdev->dev, "disable_cmf failed (%d)\n", ret);
 		break;
 	case 1:
 		ret = enable_cmf(cdev);
-		if (ret && ret != -EBUSY)
-			dev_info(&cdev->dev, "enable_cmf failed (%d)\n", ret);
 		break;
 	}
 
@@ -1344,8 +1337,7 @@
 	 * to basic mode.
 	 */
 	if (format == CMF_AUTODETECT) {
-		if (!css_characteristics_avail ||
-		    !css_general_characteristics.ext_mb) {
+		if (!css_general_characteristics.ext_mb) {
 			format = CMF_BASIC;
 		} else {
 			format = CMF_EXTENDED;
@@ -1365,8 +1357,6 @@
 		cmbops = &cmbops_extended;
 		break;
 	default:
-		printk(KERN_ERR "cio: Invalid format %d for channel "
-			"measurement facility\n", format);
 		return 1;
 	}
 
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index a769565..46c021d 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -2,8 +2,7 @@
  *  drivers/s390/cio/css.c
  *  driver for channel subsystem
  *
- *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- *			 IBM Corporation
+ *    Copyright IBM Corp. 2002,2008
  *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
  *		 Cornelia Huck (cornelia.huck@de.ibm.com)
  */
@@ -14,7 +13,9 @@
 #include <linux/errno.h>
 #include <linux/list.h>
 #include <linux/reboot.h>
+#include <asm/isc.h>
 
+#include "../s390mach.h"
 #include "css.h"
 #include "cio.h"
 #include "cio_debug.h"
@@ -30,8 +31,6 @@
 
 struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1];
 
-int css_characteristics_avail = 0;
-
 int
 for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
 {
@@ -121,25 +120,6 @@
 		kfree(sch);
 		return ERR_PTR(ret);
 	}
-
-	if (sch->st != SUBCHANNEL_TYPE_IO) {
-		/* For now we ignore all non-io subchannels. */
-		kfree(sch);
-		return ERR_PTR(-EINVAL);
-	}
-
-	/* 
-	 * Set intparm to subchannel address.
-	 * This is fine even on 64bit since the subchannel is always located
-	 * under 2G.
-	 */
-	sch->schib.pmcw.intparm = (u32)(addr_t)sch;
-	ret = cio_modify(sch);
-	if (ret) {
-		kfree(sch->lock);
-		kfree(sch);
-		return ERR_PTR(ret);
-	}
 	return sch;
 }
 
@@ -177,12 +157,18 @@
 	return ret;
 }
 
+/**
+ * css_sch_device_unregister - unregister a subchannel
+ * @sch: subchannel to be unregistered
+ */
 void css_sch_device_unregister(struct subchannel *sch)
 {
 	mutex_lock(&sch->reg_mutex);
-	device_unregister(&sch->dev);
+	if (device_is_registered(&sch->dev))
+		device_unregister(&sch->dev);
 	mutex_unlock(&sch->reg_mutex);
 }
+EXPORT_SYMBOL_GPL(css_sch_device_unregister);
 
 static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
 {
@@ -229,6 +215,41 @@
 	}
 }
 
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct subchannel *sch = to_subchannel(dev);
+
+	return sprintf(buf, "%01x\n", sch->st);
+}
+
+static DEVICE_ATTR(type, 0444, type_show, NULL);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct subchannel *sch = to_subchannel(dev);
+
+	return sprintf(buf, "css:t%01X\n", sch->st);
+}
+
+static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
+
+static struct attribute *subch_attrs[] = {
+	&dev_attr_type.attr,
+	&dev_attr_modalias.attr,
+	NULL,
+};
+
+static struct attribute_group subch_attr_group = {
+	.attrs = subch_attrs,
+};
+
+static struct attribute_group *default_subch_attr_groups[] = {
+	&subch_attr_group,
+	NULL,
+};
+
 static int css_register_subchannel(struct subchannel *sch)
 {
 	int ret;
@@ -237,16 +258,17 @@
 	sch->dev.parent = &channel_subsystems[0]->device;
 	sch->dev.bus = &css_bus_type;
 	sch->dev.release = &css_subchannel_release;
-	sch->dev.groups = subch_attr_groups;
+	sch->dev.groups = default_subch_attr_groups;
 	/*
 	 * We don't want to generate uevents for I/O subchannels that don't
 	 * have a working ccw device behind them since they will be
 	 * unregistered before they can be used anyway, so we delay the add
 	 * uevent until after device recognition was successful.
+	 * Note that we suppress the uevent for all subchannel types;
+	 * the subchannel driver can decide itself when it wants to inform
+	 * userspace of its existence.
 	 */
-	if (!cio_is_console(sch->schid))
-		/* Console is special, no need to suppress. */
-		sch->dev.uevent_suppress = 1;
+	sch->dev.uevent_suppress = 1;
 	css_update_ssd_info(sch);
 	/* make it known to the system */
 	ret = css_sch_device_register(sch);
@@ -255,10 +277,19 @@
 			      sch->schid.ssid, sch->schid.sch_no, ret);
 		return ret;
 	}
+	if (!sch->driver) {
+		/*
+		 * No driver matched. Generate the uevent now so that
+		 * a fitting driver module may be loaded based on the
+		 * modalias.
+		 */
+		sch->dev.uevent_suppress = 0;
+		kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
+	}
 	return ret;
 }
 
-static int css_probe_device(struct subchannel_id schid)
+int css_probe_device(struct subchannel_id schid)
 {
 	int ret;
 	struct subchannel *sch;
@@ -301,116 +332,12 @@
 {
 	if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv)
 		return 0;
+	if ((schib->pmcw.st == SUBCHANNEL_TYPE_MSG) && !schib->pmcw.w)
+		return 0;
 	return 1;
 }
 EXPORT_SYMBOL_GPL(css_sch_is_valid);
 
-static int css_get_subchannel_status(struct subchannel *sch)
-{
-	struct schib schib;
-
-	if (stsch(sch->schid, &schib))
-		return CIO_GONE;
-	if (!css_sch_is_valid(&schib))
-		return CIO_GONE;
-	if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
-		return CIO_REVALIDATE;
-	if (!sch->lpm)
-		return CIO_NO_PATH;
-	return CIO_OPER;
-}
-
-static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
-{
-	int event, ret, disc;
-	unsigned long flags;
-	enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
-
-	spin_lock_irqsave(sch->lock, flags);
-	disc = device_is_disconnected(sch);
-	if (disc && slow) {
-		/* Disconnected devices are evaluated directly only.*/
-		spin_unlock_irqrestore(sch->lock, flags);
-		return 0;
-	}
-	/* No interrupt after machine check - kill pending timers. */
-	device_kill_pending_timer(sch);
-	if (!disc && !slow) {
-		/* Non-disconnected devices are evaluated on the slow path. */
-		spin_unlock_irqrestore(sch->lock, flags);
-		return -EAGAIN;
-	}
-	event = css_get_subchannel_status(sch);
-	CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
-		      sch->schid.ssid, sch->schid.sch_no, event,
-		      disc ? "disconnected" : "normal",
-		      slow ? "slow" : "fast");
-	/* Analyze subchannel status. */
-	action = NONE;
-	switch (event) {
-	case CIO_NO_PATH:
-		if (disc) {
-			/* Check if paths have become available. */
-			action = REPROBE;
-			break;
-		}
-		/* fall through */
-	case CIO_GONE:
-		/* Prevent unwanted effects when opening lock. */
-		cio_disable_subchannel(sch);
-		device_set_disconnected(sch);
-		/* Ask driver what to do with device. */
-		action = UNREGISTER;
-		if (sch->driver && sch->driver->notify) {
-			spin_unlock_irqrestore(sch->lock, flags);
-			ret = sch->driver->notify(sch, event);
-			spin_lock_irqsave(sch->lock, flags);
-			if (ret)
-				action = NONE;
-		}
-		break;
-	case CIO_REVALIDATE:
-		/* Device will be removed, so no notify necessary. */
-		if (disc)
-			/* Reprobe because immediate unregister might block. */
-			action = REPROBE;
-		else
-			action = UNREGISTER_PROBE;
-		break;
-	case CIO_OPER:
-		if (disc)
-			/* Get device operational again. */
-			action = REPROBE;
-		break;
-	}
-	/* Perform action. */
-	ret = 0;
-	switch (action) {
-	case UNREGISTER:
-	case UNREGISTER_PROBE:
-		/* Unregister device (will use subchannel lock). */
-		spin_unlock_irqrestore(sch->lock, flags);
-		css_sch_device_unregister(sch);
-		spin_lock_irqsave(sch->lock, flags);
-
-		/* Reset intparm to zeroes. */
-		sch->schib.pmcw.intparm = 0;
-		cio_modify(sch);
-		break;
-	case REPROBE:
-		device_trigger_reprobe(sch);
-		break;
-	default:
-		break;
-	}
-	spin_unlock_irqrestore(sch->lock, flags);
-	/* Probe if necessary. */
-	if (action == UNREGISTER_PROBE)
-		ret = css_probe_device(sch->schid);
-
-	return ret;
-}
-
 static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
 {
 	struct schib schib;
@@ -429,6 +356,21 @@
 	return css_probe_device(schid);
 }
 
+static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
+{
+	int ret = 0;
+
+	if (sch->driver) {
+		if (sch->driver->sch_event)
+			ret = sch->driver->sch_event(sch, slow);
+		else
+			dev_dbg(&sch->dev,
+				"Got subchannel machine check but "
+				"no sch_event handler provided.\n");
+	}
+	return ret;
+}
+
 static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
 {
 	struct subchannel *sch;
@@ -596,18 +538,29 @@
 /*
  * Called from the machine check handler for subchannel report words.
  */
-void css_process_crw(int rsid1, int rsid2)
+static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
 {
 	struct subchannel_id mchk_schid;
 
-	CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
-		      rsid1, rsid2);
+	if (overflow) {
+		css_schedule_eval_all();
+		return;
+	}
+	CIO_CRW_EVENT(2, "CRW0 reports slct=%d, oflw=%d, "
+		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+		      crw0->erc, crw0->rsid);
+	if (crw1)
+		CIO_CRW_EVENT(2, "CRW1 reports slct=%d, oflw=%d, "
+			      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+			      crw1->slct, crw1->oflw, crw1->chn, crw1->rsc,
+			      crw1->anc, crw1->erc, crw1->rsid);
 	init_subchannel_id(&mchk_schid);
-	mchk_schid.sch_no = rsid1;
-	if (rsid2 != 0)
-		mchk_schid.ssid = (rsid2 >> 8) & 3;
+	mchk_schid.sch_no = crw0->rsid;
+	if (crw1)
+		mchk_schid.ssid = (crw1->rsid >> 8) & 3;
 
-	/* 
+	/*
 	 * Since we are always presented with IPI in the CRW, we have to
 	 * use stsch() to find out if the subchannel in question has come
 	 * or gone.
@@ -658,7 +611,7 @@
 static void __init
 css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
 {
-	if (css_characteristics_avail && css_general_characteristics.mcss) {
+	if (css_general_characteristics.mcss) {
 		css->global_pgid.pgid_high.ext_cssid.version = 0x80;
 		css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
 	} else {
@@ -795,8 +748,6 @@
 	ret = chsc_determine_css_characteristics();
 	if (ret == -ENOMEM)
 		goto out; /* No need to continue. */
-	if (ret == 0)
-		css_characteristics_avail = 1;
 
 	ret = chsc_alloc_sei_area();
 	if (ret)
@@ -806,6 +757,10 @@
 	if (ret)
 		goto out;
 
+	ret = s390_register_crw_handler(CRW_RSC_SCH, css_process_crw);
+	if (ret)
+		goto out;
+
 	if ((ret = bus_register(&css_bus_type)))
 		goto out;
 
@@ -836,8 +791,7 @@
 		ret = device_register(&css->device);
 		if (ret)
 			goto out_free_all;
-		if (css_characteristics_avail &&
-		    css_chsc_characteristics.secm) {
+		if (css_chsc_characteristics.secm) {
 			ret = device_create_file(&css->device,
 						 &dev_attr_cm_enable);
 			if (ret)
@@ -852,7 +806,8 @@
 		goto out_pseudo;
 	css_init_done = 1;
 
-	ctl_set_bit(6, 28);
+	/* Enable default isc for I/O subchannels. */
+	isc_register(IO_SCH_ISC);
 
 	for_each_subchannel(__init_channel_subsystem, NULL);
 	return 0;
@@ -875,7 +830,7 @@
 		i--;
 		css = channel_subsystems[i];
 		device_unregister(&css->pseudo_subchannel->dev);
-		if (css_characteristics_avail && css_chsc_characteristics.secm)
+		if (css_chsc_characteristics.secm)
 			device_remove_file(&css->device,
 					   &dev_attr_cm_enable);
 		device_unregister(&css->device);
@@ -883,6 +838,7 @@
 out_bus:
 	bus_unregister(&css_bus_type);
 out:
+	s390_unregister_crw_handler(CRW_RSC_CSS);
 	chsc_free_sei_area();
 	kfree(slow_subchannel_set);
 	printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
@@ -895,19 +851,16 @@
 	return sch == to_css(sch->dev.parent)->pseudo_subchannel;
 }
 
-/*
- * find a driver for a subchannel. They identify by the subchannel
- * type with the exception that the console subchannel driver has its own
- * subchannel type although the device is an i/o subchannel
- */
-static int
-css_bus_match (struct device *dev, struct device_driver *drv)
+static int css_bus_match(struct device *dev, struct device_driver *drv)
 {
 	struct subchannel *sch = to_subchannel(dev);
 	struct css_driver *driver = to_cssdriver(drv);
+	struct css_device_id *id;
 
-	if (sch->st == driver->subchannel_type)
-		return 1;
+	for (id = driver->subchannel_type; id->match_flags; id++) {
+		if (sch->st == id->type)
+			return 1;
+	}
 
 	return 0;
 }
@@ -945,12 +898,25 @@
 		sch->driver->shutdown(sch);
 }
 
+static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct subchannel *sch = to_subchannel(dev);
+	int ret;
+
+	ret = add_uevent_var(env, "ST=%01X", sch->st);
+	if (ret)
+		return ret;
+	ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st);
+	return ret;
+}
+
 struct bus_type css_bus_type = {
 	.name     = "css",
 	.match    = css_bus_match,
 	.probe    = css_probe,
 	.remove   = css_remove,
 	.shutdown = css_shutdown,
+	.uevent   = css_uevent,
 };
 
 /**
@@ -985,4 +951,3 @@
 
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(css_bus_type);
-EXPORT_SYMBOL_GPL(css_characteristics_avail);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index e191351..57ebf12 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -9,8 +9,7 @@
 
 #include <asm/cio.h>
 #include <asm/chpid.h>
-
-#include "schid.h"
+#include <asm/schid.h>
 
 /*
  * path grouping stuff
@@ -58,20 +57,28 @@
 	__u32 tod_high;		/* high word TOD clock */
 } __attribute__ ((packed));
 
-/*
- * A css driver handles all subchannels of one type.
- * Currently, we only care about I/O subchannels (type 0), these
- * have a ccw_device connected to them.
- */
 struct subchannel;
+struct chp_link;
+/**
+ * struct css_driver - device driver for subchannels
+ * @owner: owning module
+ * @subchannel_type: subchannel type supported by this driver
+ * @drv: embedded device driver structure
+ * @irq: called on interrupts
+ * @chp_event: called for events affecting a channel path
+ * @sch_event: called for events affecting the subchannel
+ * @probe: function called on probe
+ * @remove: function called on remove
+ * @shutdown: called at device shutdown
+ * @name: name of the device driver
+ */
 struct css_driver {
 	struct module *owner;
-	unsigned int subchannel_type;
+	struct css_device_id *subchannel_type;
 	struct device_driver drv;
 	void (*irq)(struct subchannel *);
-	int (*notify)(struct subchannel *, int);
-	void (*verify)(struct subchannel *);
-	void (*termination)(struct subchannel *);
+	int (*chp_event)(struct subchannel *, struct chp_link *, int);
+	int (*sch_event)(struct subchannel *, int);
 	int (*probe)(struct subchannel *);
 	int (*remove)(struct subchannel *);
 	void (*shutdown)(struct subchannel *);
@@ -89,13 +96,13 @@
 extern void css_driver_unregister(struct css_driver *);
 
 extern void css_sch_device_unregister(struct subchannel *);
-extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
+extern int css_probe_device(struct subchannel_id);
+extern struct subchannel *get_subchannel_by_schid(struct subchannel_id);
 extern int css_init_done;
 int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
 			       int (*fn_unknown)(struct subchannel_id,
 			       void *), void *data);
 extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
-extern void css_process_crw(int, int);
 extern void css_reiterate_subchannels(void);
 void css_update_ssd_info(struct subchannel *sch);
 
@@ -121,20 +128,6 @@
 extern struct bus_type css_bus_type;
 extern struct channel_subsystem *channel_subsystems[];
 
-/* Some helper functions for disconnected state. */
-int device_is_disconnected(struct subchannel *);
-void device_set_disconnected(struct subchannel *);
-void device_trigger_reprobe(struct subchannel *);
-
-/* Helper functions for vary on/off. */
-int device_is_online(struct subchannel *);
-void device_kill_io(struct subchannel *);
-void device_set_intretry(struct subchannel *sch);
-int device_trigger_verify(struct subchannel *sch);
-
-/* Machine check helper function. */
-void device_kill_pending_timer(struct subchannel *);
-
 /* Helper functions to build lists for the slow path. */
 void css_schedule_eval(struct subchannel_id schid);
 void css_schedule_eval_all(void);
@@ -145,6 +138,4 @@
 
 extern struct workqueue_struct *slow_path_wq;
 void css_wait_for_slow_path(void);
-
-extern struct attribute_group *subch_attr_groups[];
 #endif
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e22813d..e818d0c 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -2,8 +2,7 @@
  *  drivers/s390/cio/device.c
  *  bus driver for ccw devices
  *
- *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- *			 IBM Corporation
+ *    Copyright IBM Corp. 2002,2008
  *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
  *		 Cornelia Huck (cornelia.huck@de.ibm.com)
  *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
@@ -23,7 +22,9 @@
 #include <asm/cio.h>
 #include <asm/param.h>		/* HZ */
 #include <asm/cmb.h>
+#include <asm/isc.h>
 
+#include "chp.h"
 #include "cio.h"
 #include "cio_debug.h"
 #include "css.h"
@@ -125,19 +126,24 @@
 static void io_subchannel_irq(struct subchannel *);
 static int io_subchannel_probe(struct subchannel *);
 static int io_subchannel_remove(struct subchannel *);
-static int io_subchannel_notify(struct subchannel *, int);
-static void io_subchannel_verify(struct subchannel *);
-static void io_subchannel_ioterm(struct subchannel *);
 static void io_subchannel_shutdown(struct subchannel *);
+static int io_subchannel_sch_event(struct subchannel *, int);
+static int io_subchannel_chp_event(struct subchannel *, struct chp_link *,
+				   int);
+
+static struct css_device_id io_subchannel_ids[] = {
+	{ .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(css, io_subchannel_ids);
 
 static struct css_driver io_subchannel_driver = {
 	.owner = THIS_MODULE,
-	.subchannel_type = SUBCHANNEL_TYPE_IO,
+	.subchannel_type = io_subchannel_ids,
 	.name = "io_subchannel",
 	.irq = io_subchannel_irq,
-	.notify = io_subchannel_notify,
-	.verify = io_subchannel_verify,
-	.termination = io_subchannel_ioterm,
+	.sch_event = io_subchannel_sch_event,
+	.chp_event = io_subchannel_chp_event,
 	.probe = io_subchannel_probe,
 	.remove = io_subchannel_remove,
 	.shutdown = io_subchannel_shutdown,
@@ -487,25 +493,22 @@
 		ccw_device_set_online(cdev);
 	return 0;
 }
-static void online_store_handle_online(struct ccw_device *cdev, int force)
+static int online_store_handle_online(struct ccw_device *cdev, int force)
 {
 	int ret;
 
 	ret = online_store_recog_and_online(cdev);
 	if (ret)
-		return;
+		return ret;
 	if (force && cdev->private->state == DEV_STATE_BOXED) {
 		ret = ccw_device_stlck(cdev);
-		if (ret) {
-			dev_warn(&cdev->dev,
-				 "ccw_device_stlck returned %d!\n", ret);
-			return;
-		}
+		if (ret)
+			return ret;
 		if (cdev->id.cu_type == 0)
 			cdev->private->state = DEV_STATE_NOT_OPER;
 		online_store_recog_and_online(cdev);
 	}
-
+	return 0;
 }
 
 static ssize_t online_store (struct device *dev, struct device_attribute *attr,
@@ -538,8 +541,9 @@
 		ret = count;
 		break;
 	case 1:
-		online_store_handle_online(cdev, force);
-		ret = count;
+		ret = online_store_handle_online(cdev, force);
+		if (!ret)
+			ret = count;
 		break;
 	default:
 		ret = -EINVAL;
@@ -584,19 +588,14 @@
 static DEVICE_ATTR(online, 0644, online_show, online_store);
 static DEVICE_ATTR(availability, 0444, available_show, NULL);
 
-static struct attribute * subch_attrs[] = {
+static struct attribute *io_subchannel_attrs[] = {
 	&dev_attr_chpids.attr,
 	&dev_attr_pimpampom.attr,
 	NULL,
 };
 
-static struct attribute_group subch_attr_group = {
-	.attrs = subch_attrs,
-};
-
-struct attribute_group *subch_attr_groups[] = {
-	&subch_attr_group,
-	NULL,
+static struct attribute_group io_subchannel_attr_group = {
+	.attrs = io_subchannel_attrs,
 };
 
 static struct attribute * ccwdev_attrs[] = {
@@ -790,7 +789,7 @@
 	sch_set_cdev(sch, cdev);
 	cdev->private->schid = sch->schid;
 	cdev->ccwlock = sch->lock;
-	device_trigger_reprobe(sch);
+	ccw_device_trigger_reprobe(cdev);
 	spin_unlock_irq(sch->lock);
 }
 
@@ -1037,7 +1036,6 @@
 	struct ccw_device_private *priv;
 
 	sch_set_cdev(sch, cdev);
-	sch->driver = &io_subchannel_driver;
 	cdev->ccwlock = sch->lock;
 
 	/* Init private data. */
@@ -1122,8 +1120,33 @@
 		dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
 }
 
-static int
-io_subchannel_probe (struct subchannel *sch)
+static void io_subchannel_init_fields(struct subchannel *sch)
+{
+	if (cio_is_console(sch->schid))
+		sch->opm = 0xff;
+	else
+		sch->opm = chp_get_sch_opm(sch);
+	sch->lpm = sch->schib.pmcw.pam & sch->opm;
+	sch->isc = cio_is_console(sch->schid) ? CONSOLE_ISC : IO_SCH_ISC;
+
+	CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X"
+		      " - PIM = %02X, PAM = %02X, POM = %02X\n",
+		      sch->schib.pmcw.dev, sch->schid.ssid,
+		      sch->schid.sch_no, sch->schib.pmcw.pim,
+		      sch->schib.pmcw.pam, sch->schib.pmcw.pom);
+	/* Initially set up some fields in the pmcw. */
+	sch->schib.pmcw.ena = 0;
+	sch->schib.pmcw.csense = 1;	/* concurrent sense */
+	if ((sch->lpm & (sch->lpm - 1)) != 0)
+		sch->schib.pmcw.mp = 1; /* multipath mode */
+	/* clean up possible residual cmf stuff */
+	sch->schib.pmcw.mme = 0;
+	sch->schib.pmcw.mbfc = 0;
+	sch->schib.pmcw.mbi = 0;
+	sch->schib.mba = 0;
+}
+
+static int io_subchannel_probe(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 	int rc;
@@ -1132,11 +1155,21 @@
 
 	cdev = sch_get_cdev(sch);
 	if (cdev) {
+		rc = sysfs_create_group(&sch->dev.kobj,
+					&io_subchannel_attr_group);
+		if (rc)
+			CIO_MSG_EVENT(0, "Failed to create io subchannel "
+				      "attributes for subchannel "
+				      "0.%x.%04x (rc=%d)\n",
+				      sch->schid.ssid, sch->schid.sch_no, rc);
 		/*
 		 * This subchannel already has an associated ccw_device.
-		 * Register it and exit. This happens for all early
-		 * device, e.g. the console.
+		 * Throw the delayed uevent for the subchannel, register
+		 * the ccw_device and exit. This happens for all early
+		 * devices, e.g. the console.
 		 */
+		sch->dev.uevent_suppress = 0;
+		kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
 		cdev->dev.groups = ccwdev_attr_groups;
 		device_initialize(&cdev->dev);
 		ccw_device_register(cdev);
@@ -1152,17 +1185,24 @@
 			get_device(&cdev->dev);
 		return 0;
 	}
+	io_subchannel_init_fields(sch);
 	/*
 	 * First check if a fitting device may be found amongst the
 	 * disconnected devices or in the orphanage.
 	 */
 	dev_id.devno = sch->schib.pmcw.dev;
 	dev_id.ssid = sch->schid.ssid;
+	rc = sysfs_create_group(&sch->dev.kobj,
+				&io_subchannel_attr_group);
+	if (rc)
+		return rc;
 	/* Allocate I/O subchannel private data. */
 	sch->private = kzalloc(sizeof(struct io_subchannel_private),
 			       GFP_KERNEL | GFP_DMA);
-	if (!sch->private)
-		return -ENOMEM;
+	if (!sch->private) {
+		rc = -ENOMEM;
+		goto out_err;
+	}
 	cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
 	if (!cdev)
 		cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1181,8 +1221,8 @@
 	}
 	cdev = io_subchannel_create_ccwdev(sch);
 	if (IS_ERR(cdev)) {
-		kfree(sch->private);
-		return PTR_ERR(cdev);
+		rc = PTR_ERR(cdev);
+		goto out_err;
 	}
 	rc = io_subchannel_recog(cdev, sch);
 	if (rc) {
@@ -1191,9 +1231,12 @@
 		spin_unlock_irqrestore(sch->lock, flags);
 		if (cdev->dev.release)
 			cdev->dev.release(&cdev->dev);
-		kfree(sch->private);
+		goto out_err;
 	}
-
+	return 0;
+out_err:
+	kfree(sch->private);
+	sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
 	return rc;
 }
 
@@ -1214,6 +1257,7 @@
 	ccw_device_unregister(cdev);
 	put_device(&cdev->dev);
 	kfree(sch->private);
+	sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
 	return 0;
 }
 
@@ -1224,11 +1268,7 @@
 	cdev = sch_get_cdev(sch);
 	if (!cdev)
 		return 0;
-	if (!cdev->drv)
-		return 0;
-	if (!cdev->online)
-		return 0;
-	return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+	return ccw_device_notify(cdev, event);
 }
 
 static void io_subchannel_verify(struct subchannel *sch)
@@ -1240,20 +1280,94 @@
 		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
 }
 
-static void io_subchannel_ioterm(struct subchannel *sch)
+static int check_for_io_on_path(struct subchannel *sch, int mask)
+{
+	int cc;
+
+	cc = stsch(sch->schid, &sch->schib);
+	if (cc)
+		return 0;
+	if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
+		return 1;
+	return 0;
+}
+
+static void terminate_internal_io(struct subchannel *sch,
+				  struct ccw_device *cdev)
+{
+	if (cio_clear(sch)) {
+		/* Recheck device in case clear failed. */
+		sch->lpm = 0;
+		if (cdev->online)
+			dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+		else
+			css_schedule_eval(sch->schid);
+		return;
+	}
+	cdev->private->state = DEV_STATE_CLEAR_VERIFY;
+	/* Request retry of internal operation. */
+	cdev->private->flags.intretry = 1;
+	/* Call handler. */
+	if (cdev->handler)
+		cdev->handler(cdev, cdev->private->intparm,
+			      ERR_PTR(-EIO));
+}
+
+static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
 {
 	struct ccw_device *cdev;
 
 	cdev = sch_get_cdev(sch);
 	if (!cdev)
 		return;
-	/* Internal I/O will be retried by the interrupt handler. */
-	if (cdev->private->flags.intretry)
-		return;
-	cdev->private->state = DEV_STATE_CLEAR_VERIFY;
-	if (cdev->handler)
-		cdev->handler(cdev, cdev->private->intparm,
-			      ERR_PTR(-EIO));
+	if (check_for_io_on_path(sch, mask)) {
+		if (cdev->private->state == DEV_STATE_ONLINE)
+			ccw_device_kill_io(cdev);
+		else {
+			terminate_internal_io(sch, cdev);
+			/* Re-start path verification. */
+			dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+		}
+	} else
+		/* trigger path verification. */
+		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+
+}
+
+static int io_subchannel_chp_event(struct subchannel *sch,
+				   struct chp_link *link, int event)
+{
+	int mask;
+
+	mask = chp_ssd_get_mask(&sch->ssd_info, link);
+	if (!mask)
+		return 0;
+	switch (event) {
+	case CHP_VARY_OFF:
+		sch->opm &= ~mask;
+		sch->lpm &= ~mask;
+		io_subchannel_terminate_path(sch, mask);
+		break;
+	case CHP_VARY_ON:
+		sch->opm |= mask;
+		sch->lpm |= mask;
+		io_subchannel_verify(sch);
+		break;
+	case CHP_OFFLINE:
+		if (stsch(sch->schid, &sch->schib))
+			return -ENXIO;
+		if (!css_sch_is_valid(&sch->schib))
+			return -ENODEV;
+		io_subchannel_terminate_path(sch, mask);
+		break;
+	case CHP_ONLINE:
+		if (stsch(sch->schid, &sch->schib))
+			return -ENXIO;
+		sch->lpm |= mask & sch->opm;
+		io_subchannel_verify(sch);
+		break;
+	}
+	return 0;
 }
 
 static void
@@ -1285,6 +1399,195 @@
 	cio_disable_subchannel(sch);
 }
 
+static int io_subchannel_get_status(struct subchannel *sch)
+{
+	struct schib schib;
+
+	if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
+		return CIO_GONE;
+	if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
+		return CIO_REVALIDATE;
+	if (!sch->lpm)
+		return CIO_NO_PATH;
+	return CIO_OPER;
+}
+
+static int device_is_disconnected(struct ccw_device *cdev)
+{
+	if (!cdev)
+		return 0;
+	return (cdev->private->state == DEV_STATE_DISCONNECTED ||
+		cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
+}
+
+static int recovery_check(struct device *dev, void *data)
+{
+	struct ccw_device *cdev = to_ccwdev(dev);
+	int *redo = data;
+
+	spin_lock_irq(cdev->ccwlock);
+	switch (cdev->private->state) {
+	case DEV_STATE_DISCONNECTED:
+		CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
+			      cdev->private->dev_id.ssid,
+			      cdev->private->dev_id.devno);
+		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+		*redo = 1;
+		break;
+	case DEV_STATE_DISCONNECTED_SENSE_ID:
+		*redo = 1;
+		break;
+	}
+	spin_unlock_irq(cdev->ccwlock);
+
+	return 0;
+}
+
+static void recovery_work_func(struct work_struct *unused)
+{
+	int redo = 0;
+
+	bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
+	if (redo) {
+		spin_lock_irq(&recovery_lock);
+		if (!timer_pending(&recovery_timer)) {
+			if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
+				recovery_phase++;
+			mod_timer(&recovery_timer, jiffies +
+				  recovery_delay[recovery_phase] * HZ);
+		}
+		spin_unlock_irq(&recovery_lock);
+	} else
+		CIO_MSG_EVENT(4, "recovery: end\n");
+}
+
+static DECLARE_WORK(recovery_work, recovery_work_func);
+
+static void recovery_func(unsigned long data)
+{
+	/*
+	 * We can't do our recovery in softirq context and it's not
+	 * performance critical, so we schedule it.
+	 */
+	schedule_work(&recovery_work);
+}
+
+static void ccw_device_schedule_recovery(void)
+{
+	unsigned long flags;
+
+	CIO_MSG_EVENT(4, "recovery: schedule\n");
+	spin_lock_irqsave(&recovery_lock, flags);
+	if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
+		recovery_phase = 0;
+		mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
+	}
+	spin_unlock_irqrestore(&recovery_lock, flags);
+}
+
+static void device_set_disconnected(struct ccw_device *cdev)
+{
+	if (!cdev)
+		return;
+	ccw_device_set_timeout(cdev, 0);
+	cdev->private->flags.fake_irb = 0;
+	cdev->private->state = DEV_STATE_DISCONNECTED;
+	if (cdev->online)
+		ccw_device_schedule_recovery();
+}
+
+static int io_subchannel_sch_event(struct subchannel *sch, int slow)
+{
+	int event, ret, disc;
+	unsigned long flags;
+	enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
+	struct ccw_device *cdev;
+
+	spin_lock_irqsave(sch->lock, flags);
+	cdev = sch_get_cdev(sch);
+	disc = device_is_disconnected(cdev);
+	if (disc && slow) {
+		/* Disconnected devices are evaluated directly only.*/
+		spin_unlock_irqrestore(sch->lock, flags);
+		return 0;
+	}
+	/* No interrupt after machine check - kill pending timers. */
+	if (cdev)
+		ccw_device_set_timeout(cdev, 0);
+	if (!disc && !slow) {
+		/* Non-disconnected devices are evaluated on the slow path. */
+		spin_unlock_irqrestore(sch->lock, flags);
+		return -EAGAIN;
+	}
+	event = io_subchannel_get_status(sch);
+	CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
+		      sch->schid.ssid, sch->schid.sch_no, event,
+		      disc ? "disconnected" : "normal",
+		      slow ? "slow" : "fast");
+	/* Analyze subchannel status. */
+	action = NONE;
+	switch (event) {
+	case CIO_NO_PATH:
+		if (disc) {
+			/* Check if paths have become available. */
+			action = REPROBE;
+			break;
+		}
+		/* fall through */
+	case CIO_GONE:
+		/* Prevent unwanted effects when opening lock. */
+		cio_disable_subchannel(sch);
+		device_set_disconnected(cdev);
+		/* Ask driver what to do with device. */
+		action = UNREGISTER;
+		spin_unlock_irqrestore(sch->lock, flags);
+		ret = io_subchannel_notify(sch, event);
+		spin_lock_irqsave(sch->lock, flags);
+		if (ret)
+			action = NONE;
+		break;
+	case CIO_REVALIDATE:
+		/* Device will be removed, so no notify necessary. */
+		if (disc)
+			/* Reprobe because immediate unregister might block. */
+			action = REPROBE;
+		else
+			action = UNREGISTER_PROBE;
+		break;
+	case CIO_OPER:
+		if (disc)
+			/* Get device operational again. */
+			action = REPROBE;
+		break;
+	}
+	/* Perform action. */
+	ret = 0;
+	switch (action) {
+	case UNREGISTER:
+	case UNREGISTER_PROBE:
+		/* Unregister device (will use subchannel lock). */
+		spin_unlock_irqrestore(sch->lock, flags);
+		css_sch_device_unregister(sch);
+		spin_lock_irqsave(sch->lock, flags);
+
+		/* Reset intparm to zeroes. */
+		sch->schib.pmcw.intparm = 0;
+		cio_modify(sch);
+		break;
+	case REPROBE:
+		ccw_device_trigger_reprobe(cdev);
+		break;
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(sch->lock, flags);
+	/* Probe if necessary. */
+	if (action == UNREGISTER_PROBE)
+		ret = css_probe_device(sch->schid);
+
+	return ret;
+}
+
 #ifdef CONFIG_CCW_CONSOLE
 static struct ccw_device console_cdev;
 static struct ccw_device_private console_private;
@@ -1297,14 +1600,16 @@
 	return &ccw_console_lock;
 }
 
-static int
-ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
+static int ccw_device_console_enable(struct ccw_device *cdev,
+				     struct subchannel *sch)
 {
 	int rc;
 
 	/* Attach subchannel private data. */
 	sch->private = cio_get_console_priv();
 	memset(sch->private, 0, sizeof(struct io_subchannel_private));
+	io_subchannel_init_fields(sch);
+	sch->driver = &io_subchannel_driver;
 	/* Initialize the ccw_device structure. */
 	cdev->dev.parent= &sch->dev;
 	rc = io_subchannel_recog(cdev, sch);
@@ -1515,71 +1820,6 @@
 	return sch->schid;
 }
 
-static int recovery_check(struct device *dev, void *data)
-{
-	struct ccw_device *cdev = to_ccwdev(dev);
-	int *redo = data;
-
-	spin_lock_irq(cdev->ccwlock);
-	switch (cdev->private->state) {
-	case DEV_STATE_DISCONNECTED:
-		CIO_MSG_EVENT(4, "recovery: trigger 0.%x.%04x\n",
-			      cdev->private->dev_id.ssid,
-			      cdev->private->dev_id.devno);
-		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
-		*redo = 1;
-		break;
-	case DEV_STATE_DISCONNECTED_SENSE_ID:
-		*redo = 1;
-		break;
-	}
-	spin_unlock_irq(cdev->ccwlock);
-
-	return 0;
-}
-
-static void recovery_work_func(struct work_struct *unused)
-{
-	int redo = 0;
-
-	bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
-	if (redo) {
-		spin_lock_irq(&recovery_lock);
-		if (!timer_pending(&recovery_timer)) {
-			if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
-				recovery_phase++;
-			mod_timer(&recovery_timer, jiffies +
-				  recovery_delay[recovery_phase] * HZ);
-		}
-		spin_unlock_irq(&recovery_lock);
-	} else
-		CIO_MSG_EVENT(4, "recovery: end\n");
-}
-
-static DECLARE_WORK(recovery_work, recovery_work_func);
-
-static void recovery_func(unsigned long data)
-{
-	/*
-	 * We can't do our recovery in softirq context and it's not
-	 * performance critical, so we schedule it.
-	 */
-	schedule_work(&recovery_work);
-}
-
-void ccw_device_schedule_recovery(void)
-{
-	unsigned long flags;
-
-	CIO_MSG_EVENT(4, "recovery: schedule\n");
-	spin_lock_irqsave(&recovery_lock, flags);
-	if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
-		recovery_phase = 0;
-		mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
-	}
-	spin_unlock_irqrestore(&recovery_lock, flags);
-}
-
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(ccw_device_set_online);
 EXPORT_SYMBOL(ccw_device_set_offline);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index cb08092..9800a83 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -88,8 +88,6 @@
 int ccw_device_online(struct ccw_device *);
 int ccw_device_offline(struct ccw_device *);
 
-void ccw_device_schedule_recovery(void);
-
 /* Function prototypes for device status and basic sense stuff. */
 void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
 void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
@@ -118,6 +116,11 @@
 
 int ccw_device_stlck(struct ccw_device *);
 
+/* Helper function for machine check handling. */
+void ccw_device_trigger_reprobe(struct ccw_device *);
+void ccw_device_kill_io(struct ccw_device *);
+int ccw_device_notify(struct ccw_device *, int);
+
 /* qdio needs this. */
 void ccw_device_set_timeout(struct ccw_device *, int);
 extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index e268d5a..8b5fe57 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -2,8 +2,7 @@
  * drivers/s390/cio/device_fsm.c
  * finite state machine for device handling
  *
- *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- *			 IBM Corporation
+ *    Copyright IBM Corp. 2002,2008
  *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
  *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
  */
@@ -27,65 +26,6 @@
 
 static int timeout_log_enabled;
 
-int
-device_is_online(struct subchannel *sch)
-{
-	struct ccw_device *cdev;
-
-	cdev = sch_get_cdev(sch);
-	if (!cdev)
-		return 0;
-	return (cdev->private->state == DEV_STATE_ONLINE);
-}
-
-int
-device_is_disconnected(struct subchannel *sch)
-{
-	struct ccw_device *cdev;
-
-	cdev = sch_get_cdev(sch);
-	if (!cdev)
-		return 0;
-	return (cdev->private->state == DEV_STATE_DISCONNECTED ||
-		cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
-}
-
-void
-device_set_disconnected(struct subchannel *sch)
-{
-	struct ccw_device *cdev;
-
-	cdev = sch_get_cdev(sch);
-	if (!cdev)
-		return;
-	ccw_device_set_timeout(cdev, 0);
-	cdev->private->flags.fake_irb = 0;
-	cdev->private->state = DEV_STATE_DISCONNECTED;
-	if (cdev->online)
-		ccw_device_schedule_recovery();
-}
-
-void device_set_intretry(struct subchannel *sch)
-{
-	struct ccw_device *cdev;
-
-	cdev = sch_get_cdev(sch);
-	if (!cdev)
-		return;
-	cdev->private->flags.intretry = 1;
-}
-
-int device_trigger_verify(struct subchannel *sch)
-{
-	struct ccw_device *cdev;
-
-	cdev = sch_get_cdev(sch);
-	if (!cdev || !cdev->online)
-		return -EINVAL;
-	dev_fsm_event(cdev, DEV_EVENT_VERIFY);
-	return 0;
-}
-
 static int __init ccw_timeout_log_setup(char *unused)
 {
 	timeout_log_enabled = 1;
@@ -99,31 +39,43 @@
 	struct schib schib;
 	struct subchannel *sch;
 	struct io_subchannel_private *private;
+	union orb *orb;
 	int cc;
 
 	sch = to_subchannel(cdev->dev.parent);
 	private = to_io_private(sch);
+	orb = &private->orb;
 	cc = stsch(sch->schid, &schib);
 
 	printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
 	       "device information:\n", get_clock());
 	printk(KERN_WARNING "cio: orb:\n");
 	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
-		       &private->orb, sizeof(private->orb), 0);
+		       orb, sizeof(*orb), 0);
 	printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
 	printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
 	printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
 	       "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
 
-	if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw ||
-	    (void *)(addr_t)private->orb.cpa == cdev->private->iccws)
-		printk(KERN_WARNING "cio: last channel program (intern):\n");
-	else
-		printk(KERN_WARNING "cio: last channel program:\n");
+	if (orb->tm.b) {
+		printk(KERN_WARNING "cio: orb indicates transport mode\n");
+		printk(KERN_WARNING "cio: last tcw:\n");
+		print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+			       (void *)(addr_t)orb->tm.tcw,
+			       sizeof(struct tcw), 0);
+	} else {
+		printk(KERN_WARNING "cio: orb indicates command mode\n");
+		if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
+		    (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
+			printk(KERN_WARNING "cio: last channel program "
+			       "(intern):\n");
+		else
+			printk(KERN_WARNING "cio: last channel program:\n");
 
-	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
-		       (void *)(addr_t)private->orb.cpa,
-		       sizeof(struct ccw1), 0);
+		print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+			       (void *)(addr_t)orb->cmd.cpa,
+			       sizeof(struct ccw1), 0);
+	}
 	printk(KERN_WARNING "cio: ccw device state: %d\n",
 	       cdev->private->state);
 	printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
@@ -171,18 +123,6 @@
 	add_timer(&cdev->private->timer);
 }
 
-/* Kill any pending timers after machine check. */
-void
-device_kill_pending_timer(struct subchannel *sch)
-{
-	struct ccw_device *cdev;
-
-	cdev = sch_get_cdev(sch);
-	if (!cdev)
-		return;
-	ccw_device_set_timeout(cdev, 0);
-}
-
 /*
  * Cancel running i/o. This is called repeatedly since halt/clear are
  * asynchronous operations. We do one try with cio_cancel, two tries
@@ -205,15 +145,18 @@
 		/* Not operational -> done. */
 		return 0;
 	/* Stage 1: cancel io. */
-	if (!(sch->schib.scsw.actl & SCSW_ACTL_HALT_PEND) &&
-	    !(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
-		ret = cio_cancel(sch);
-		if (ret != -EINVAL)
-			return ret;
-		/* cancel io unsuccessful. From now on it is asynchronous. */
+	if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
+	    !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
+		if (!scsw_is_tm(&sch->schib.scsw)) {
+			ret = cio_cancel(sch);
+			if (ret != -EINVAL)
+				return ret;
+		}
+		/* cancel io unsuccessful or not applicable (transport mode).
+		 * Continue with asynchronous instructions. */
 		cdev->private->iretry = 3;	/* 3 halt retries. */
 	}
-	if (!(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
+	if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
 		/* Stage 2: halt io. */
 		if (cdev->private->iretry) {
 			cdev->private->iretry--;
@@ -388,34 +331,30 @@
 	}
 }
 
+int ccw_device_notify(struct ccw_device *cdev, int event)
+{
+	if (!cdev->drv)
+		return 0;
+	if (!cdev->online)
+		return 0;
+	return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+}
+
 static void
 ccw_device_oper_notify(struct work_struct *work)
 {
 	struct ccw_device_private *priv;
 	struct ccw_device *cdev;
-	struct subchannel *sch;
 	int ret;
-	unsigned long flags;
 
 	priv = container_of(work, struct ccw_device_private, kick_work);
 	cdev = priv->cdev;
-	spin_lock_irqsave(cdev->ccwlock, flags);
-	sch = to_subchannel(cdev->dev.parent);
-	if (sch->driver && sch->driver->notify) {
-		spin_unlock_irqrestore(cdev->ccwlock, flags);
-		ret = sch->driver->notify(sch, CIO_OPER);
-		spin_lock_irqsave(cdev->ccwlock, flags);
-	} else
-		ret = 0;
+	ret = ccw_device_notify(cdev, CIO_OPER);
 	if (ret) {
 		/* Reenable channel measurements, if needed. */
-		spin_unlock_irqrestore(cdev->ccwlock, flags);
 		cmf_reenable(cdev);
-		spin_lock_irqsave(cdev->ccwlock, flags);
 		wake_up(&cdev->private->wait_q);
-	}
-	spin_unlock_irqrestore(cdev->ccwlock, flags);
-	if (!ret)
+	} else
 		/* Driver doesn't want device back. */
 		ccw_device_do_unreg_rereg(work);
 }
@@ -621,10 +560,11 @@
 		/* Deliver fake irb to device driver, if needed. */
 		if (cdev->private->flags.fake_irb) {
 			memset(&cdev->private->irb, 0, sizeof(struct irb));
-			cdev->private->irb.scsw.cc = 1;
-			cdev->private->irb.scsw.fctl = SCSW_FCTL_START_FUNC;
-			cdev->private->irb.scsw.actl = SCSW_ACTL_START_PEND;
-			cdev->private->irb.scsw.stctl = SCSW_STCTL_STATUS_PEND;
+			cdev->private->irb.scsw.cmd.cc = 1;
+			cdev->private->irb.scsw.cmd.fctl = SCSW_FCTL_START_FUNC;
+			cdev->private->irb.scsw.cmd.actl = SCSW_ACTL_START_PEND;
+			cdev->private->irb.scsw.cmd.stctl =
+				SCSW_STCTL_STATUS_PEND;
 			cdev->private->flags.fake_irb = 0;
 			if (cdev->handler)
 				cdev->handler(cdev, cdev->private->intparm,
@@ -718,13 +658,10 @@
 	sch = to_subchannel(cdev->dev.parent);
 	if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
 		return -ENODEV;
-	if (cdev->private->state != DEV_STATE_ONLINE) {
-		if (sch->schib.scsw.actl != 0)
-			return -EBUSY;
-		return -EINVAL;
-	}
-	if (sch->schib.scsw.actl != 0)
+	if (scsw_actl(&sch->schib.scsw) != 0)
 		return -EBUSY;
+	if (cdev->private->state != DEV_STATE_ONLINE)
+		return -EINVAL;
 	/* Are we doing path grouping? */
 	if (!cdev->private->options.pgroup) {
 		/* No, set state offline immediately. */
@@ -799,9 +736,9 @@
 	 */
 	stsch(sch->schid, &sch->schib);
 
-	if (sch->schib.scsw.actl != 0 ||
-	    (sch->schib.scsw.stctl & SCSW_STCTL_STATUS_PEND) ||
-	    (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
+	if (scsw_actl(&sch->schib.scsw) != 0 ||
+	    (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
+	    (scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) {
 		/*
 		 * No final status yet or final status not yet delivered
 		 * to the device driver. Can't do path verfication now,
@@ -823,13 +760,13 @@
 ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
 {
 	struct irb *irb;
+	int is_cmd;
 
 	irb = (struct irb *) __LC_IRB;
+	is_cmd = !scsw_is_tm(&irb->scsw);
 	/* Check for unsolicited interrupt. */
-	if ((irb->scsw.stctl ==
-	    		(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS))
-	    && (!irb->scsw.cc)) {
-		if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+	if (!scsw_is_solicited(&irb->scsw)) {
+		if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
 		    !irb->esw.esw0.erw.cons) {
 			/* Unit check but no sense data. Need basic sense. */
 			if (ccw_device_do_sense(cdev, irb) != 0)
@@ -848,7 +785,7 @@
 	}
 	/* Accumulate status and find out if a basic sense is needed. */
 	ccw_device_accumulate_irb(cdev, irb);
-	if (cdev->private->flags.dosense) {
+	if (is_cmd && cdev->private->flags.dosense) {
 		if (ccw_device_do_sense(cdev, irb) == 0) {
 			cdev->private->state = DEV_STATE_W4SENSE;
 		}
@@ -892,9 +829,9 @@
 
 	irb = (struct irb *) __LC_IRB;
 	/* Check for unsolicited interrupt. */
-	if (irb->scsw.stctl ==
-	    		(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-		if (irb->scsw.cc == 1)
+	if (scsw_stctl(&irb->scsw) ==
+	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
+		if (scsw_cc(&irb->scsw) == 1)
 			/* Basic sense hasn't started. Try again. */
 			ccw_device_do_sense(cdev, irb);
 		else {
@@ -912,7 +849,8 @@
 	 * only deliver the halt/clear interrupt to the device driver as if it
 	 * had killed the original request.
 	 */
-	if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
+	if (scsw_fctl(&irb->scsw) &
+	    (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
 		/* Retry Basic Sense if requested. */
 		if (cdev->private->flags.intretry) {
 			cdev->private->flags.intretry = 0;
@@ -986,12 +924,10 @@
 			      ERR_PTR(-EIO));
 }
 
-void device_kill_io(struct subchannel *sch)
+void ccw_device_kill_io(struct ccw_device *cdev)
 {
 	int ret;
-	struct ccw_device *cdev;
 
-	cdev = sch_get_cdev(sch);
 	ret = ccw_device_cancel_halt_clear(cdev);
 	if (ret == -EBUSY) {
 		ccw_device_set_timeout(cdev, 3*HZ);
@@ -1021,9 +957,9 @@
 	case DEV_EVENT_INTERRUPT:
 		irb = (struct irb *) __LC_IRB;
 		/* Check for unsolicited interrupt. */
-		if ((irb->scsw.stctl ==
+		if ((scsw_stctl(&irb->scsw) ==
 		     (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
-		    (!irb->scsw.cc))
+		    (!scsw_cc(&irb->scsw)))
 			/* FIXME: we should restart stlck here, but this
 			 * is extremely unlikely ... */
 			goto out_wakeup;
@@ -1055,17 +991,14 @@
 	ccw_device_sense_id_start(cdev);
 }
 
-void
-device_trigger_reprobe(struct subchannel *sch)
+void ccw_device_trigger_reprobe(struct ccw_device *cdev)
 {
-	struct ccw_device *cdev;
+	struct subchannel *sch;
 
-	cdev = sch_get_cdev(sch);
-	if (!cdev)
-		return;
 	if (cdev->private->state != DEV_STATE_DISCONNECTED)
 		return;
 
+	sch = to_subchannel(cdev->dev.parent);
 	/* Update some values. */
 	if (stsch(sch->schid, &sch->schib))
 		return;
@@ -1081,7 +1014,6 @@
 	sch->schib.pmcw.ena = 0;
 	if ((sch->lpm & (sch->lpm - 1)) != 0)
 		sch->schib.pmcw.mp = 1;
-	sch->schib.pmcw.intparm = (u32)(addr_t)sch;
 	/* We should also udate ssd info, but this has to wait. */
 	/* Check if this is another device which appeared on the same sch. */
 	if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index cba70205..1bdaa61 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -196,7 +196,7 @@
 	irb = &cdev->private->irb;
 
 	/* Check the error cases. */
-	if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
 		/* Retry Sense ID if requested. */
 		if (cdev->private->flags.intretry) {
 			cdev->private->flags.intretry = 0;
@@ -234,10 +234,10 @@
 			      irb->ecw[6], irb->ecw[7]);
 		return -EAGAIN;
 	}
-	if (irb->scsw.cc == 3) {
+	if (irb->scsw.cmd.cc == 3) {
 		u8 lpm;
 
-		lpm = to_io_private(sch)->orb.lpm;
+		lpm = to_io_private(sch)->orb.cmd.lpm;
 		if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
 			CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
 				      "on subchannel 0.%x.%04x is "
@@ -248,9 +248,9 @@
 	}
 
 	/* Did we get a proper answer ? */
-	if (irb->scsw.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
+	if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
 	    cdev->private->senseid.reserved == 0xFF) {
-		if (irb->scsw.count < sizeof(struct senseid) - 8)
+		if (irb->scsw.cmd.count < sizeof(struct senseid) - 8)
 			cdev->private->flags.esid = 1;
 		return 0; /* Success */
 	}
@@ -260,7 +260,7 @@
 		      "subchannel 0.%x.%04x returns status %02X%02X\n",
 		      cdev->private->dev_id.devno, sch->schid.ssid,
 		      sch->schid.sch_no,
-		      irb->scsw.dstat, irb->scsw.cstat);
+		      irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
 	return -EAGAIN;
 }
 
@@ -277,9 +277,9 @@
 	sch = to_subchannel(cdev->dev.parent);
 	irb = (struct irb *) __LC_IRB;
 	/* Retry sense id, if needed. */
-	if (irb->scsw.stctl ==
+	if (irb->scsw.cmd.stctl ==
 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-		if ((irb->scsw.cc == 1) || !irb->scsw.actl) {
+		if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) {
 			ret = __ccw_device_sense_id_start(cdev);
 			if (ret && ret != -EBUSY)
 				ccw_device_sense_id_done(cdev, ret);
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index f308ad5..ee1a283 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -17,6 +17,7 @@
 #include <asm/ccwdev.h>
 #include <asm/idals.h>
 #include <asm/chpid.h>
+#include <asm/fcx.h>
 
 #include "cio.h"
 #include "cio_debug.h"
@@ -179,8 +180,8 @@
 			return -EBUSY;
 	}
 	if (cdev->private->state != DEV_STATE_ONLINE ||
-	    ((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
-	     !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||
+	    ((sch->schib.scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) &&
+	     !(sch->schib.scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS)) ||
 	    cdev->private->flags.doverify)
 		return -EBUSY;
 	ret = cio_set_options (sch, flags);
@@ -379,7 +380,7 @@
 	if (cdev->private->state == DEV_STATE_NOT_OPER)
 		return -ENODEV;
 	if (cdev->private->state != DEV_STATE_ONLINE ||
-	    !(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED))
+	    !(sch->schib.scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
 		return -EINVAL;
 	return cio_resume(sch);
 }
@@ -404,7 +405,7 @@
 	 *  - fast notification was requested (primary status)
 	 *  - unsolicited interrupts
 	 */
-	stctl = cdev->private->irb.scsw.stctl;
+	stctl = scsw_stctl(&cdev->private->irb.scsw);
 	ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
 		(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
 		(stctl == SCSW_STCTL_STATUS_PEND);
@@ -528,14 +529,15 @@
 		cio_disable_subchannel(sch); //FIXME: return code?
 		goto out_unlock;
 	}
-	cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND;
+	cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
 	spin_unlock_irqrestore(sch->lock, flags);
-	wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0);
+	wait_event(cdev->private->wait_q,
+		   cdev->private->irb.scsw.cmd.actl == 0);
 	spin_lock_irqsave(sch->lock, flags);
 	cio_disable_subchannel(sch); //FIXME: return code?
-	if ((cdev->private->irb.scsw.dstat !=
+	if ((cdev->private->irb.scsw.cmd.dstat !=
 	     (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
-	    (cdev->private->irb.scsw.cstat != 0))
+	    (cdev->private->irb.scsw.cmd.cstat != 0))
 		ret = -EIO;
 	/* Clear irb. */
 	memset(&cdev->private->irb, 0, sizeof(struct irb));
@@ -568,6 +570,122 @@
 }
 EXPORT_SYMBOL(ccw_device_get_id);
 
+/**
+ * ccw_device_tm_start_key - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
+			    unsigned long intparm, u8 lpm, u8 key)
+{
+	struct subchannel *sch;
+	int rc;
+
+	sch = to_subchannel(cdev->dev.parent);
+	if (cdev->private->state != DEV_STATE_ONLINE)
+		return -EIO;
+	/* Adjust requested path mask to excluded varied off paths. */
+	if (lpm) {
+		lpm &= sch->opm;
+		if (lpm == 0)
+			return -EACCES;
+	}
+	rc = cio_tm_start_key(sch, tcw, lpm, key);
+	if (rc == 0)
+		cdev->private->intparm = intparm;
+	return rc;
+}
+EXPORT_SYMBOL(ccw_device_tm_start_key);
+
+/**
+ * ccw_device_tm_start_timeout_key - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ * @expires: time span in jiffies after which to abort request
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw,
+				    unsigned long intparm, u8 lpm, u8 key,
+				    int expires)
+{
+	int ret;
+
+	ccw_device_set_timeout(cdev, expires);
+	ret = ccw_device_tm_start_key(cdev, tcw, intparm, lpm, key);
+	if (ret != 0)
+		ccw_device_set_timeout(cdev, 0);
+	return ret;
+}
+EXPORT_SYMBOL(ccw_device_tm_start_timeout_key);
+
+/**
+ * ccw_device_tm_start - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start(struct ccw_device *cdev, struct tcw *tcw,
+			unsigned long intparm, u8 lpm)
+{
+	return ccw_device_tm_start_key(cdev, tcw, intparm, lpm,
+				       PAGE_DEFAULT_KEY);
+}
+EXPORT_SYMBOL(ccw_device_tm_start);
+
+/**
+ * ccw_device_tm_start_timeout - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @expires: time span in jiffies after which to abort request
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw,
+			       unsigned long intparm, u8 lpm, int expires)
+{
+	return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm,
+					       PAGE_DEFAULT_KEY, expires);
+}
+EXPORT_SYMBOL(ccw_device_tm_start_timeout);
+
+/**
+ * ccw_device_tm_intrg - perform interrogate function
+ * @cdev: ccw device on which to perform the interrogate function
+ *
+ * Perform an interrogate function on the given ccw device. Return zero on
+ * success, non-zero otherwise.
+ */
+int ccw_device_tm_intrg(struct ccw_device *cdev)
+{
+	struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+	if (cdev->private->state != DEV_STATE_ONLINE)
+		return -EIO;
+	if (!scsw_is_tm(&sch->schib.scsw) ||
+	    !(scsw_actl(&sch->schib.scsw) | SCSW_ACTL_START_PEND))
+		return -EINVAL;
+	return cio_tm_intrg(sch);
+}
+EXPORT_SYMBOL(ccw_device_tm_intrg);
+
 // FIXME: these have to go:
 
 int
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 5cf7be0..86bc94e 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -28,13 +28,13 @@
  * Helper function called from interrupt context to decide whether an
  * operation should be tried again.
  */
-static int __ccw_device_should_retry(struct scsw *scsw)
+static int __ccw_device_should_retry(union scsw *scsw)
 {
 	/* CC is only valid if start function bit is set. */
-	if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
+	if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1)
 		return 1;
 	/* No more activity. For sense and set PGID we stubbornly try again. */
-	if (!scsw->actl)
+	if (!scsw->cmd.actl)
 		return 1;
 	return 0;
 }
@@ -125,7 +125,7 @@
 
 	sch = to_subchannel(cdev->dev.parent);
 	irb = &cdev->private->irb;
-	if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
 		/* Retry Sense PGID if requested. */
 		if (cdev->private->flags.intretry) {
 			cdev->private->flags.intretry = 0;
@@ -155,10 +155,10 @@
 			      irb->ecw[6], irb->ecw[7]);
 		return -EAGAIN;
 	}
-	if (irb->scsw.cc == 3) {
+	if (irb->scsw.cmd.cc == 3) {
 		u8 lpm;
 
-		lpm = to_io_private(sch)->orb.lpm;
+		lpm = to_io_private(sch)->orb.cmd.lpm;
 		CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x,"
 			      " lpm %02X, became 'not operational'\n",
 			      cdev->private->dev_id.devno, sch->schid.ssid,
@@ -188,7 +188,7 @@
 
 	irb = (struct irb *) __LC_IRB;
 
-	if (irb->scsw.stctl ==
+	if (irb->scsw.cmd.stctl ==
 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
 		if (__ccw_device_should_retry(&irb->scsw)) {
 			ret = __ccw_device_sense_pgid_start(cdev);
@@ -331,7 +331,7 @@
 
 	sch = to_subchannel(cdev->dev.parent);
 	irb = &cdev->private->irb;
-	if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
 		/* Retry Set PGID if requested. */
 		if (cdev->private->flags.intretry) {
 			cdev->private->flags.intretry = 0;
@@ -355,7 +355,7 @@
 			      irb->ecw[6], irb->ecw[7]);
 		return -EAGAIN;
 	}
-	if (irb->scsw.cc == 3) {
+	if (irb->scsw.cmd.cc == 3) {
 		CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x,"
 			      " lpm %02X, became 'not operational'\n",
 			      cdev->private->dev_id.devno, sch->schid.ssid,
@@ -376,7 +376,7 @@
 
 	sch = to_subchannel(cdev->dev.parent);
 	irb = &cdev->private->irb;
-	if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
 		/* Retry NOP if requested. */
 		if (cdev->private->flags.intretry) {
 			cdev->private->flags.intretry = 0;
@@ -384,7 +384,7 @@
 		}
 		return -ETIME;
 	}
-	if (irb->scsw.cc == 3) {
+	if (irb->scsw.cmd.cc == 3) {
 		CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x,"
 			      " lpm %02X, became 'not operational'\n",
 			      cdev->private->dev_id.devno, sch->schid.ssid,
@@ -438,7 +438,7 @@
 
 	irb = (struct irb *) __LC_IRB;
 
-	if (irb->scsw.stctl ==
+	if (irb->scsw.cmd.stctl ==
 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
 		if (__ccw_device_should_retry(&irb->scsw))
 			__ccw_device_verify_start(cdev);
@@ -544,7 +544,7 @@
 
 	irb = (struct irb *) __LC_IRB;
 
-	if (irb->scsw.stctl ==
+	if (irb->scsw.cmd.stctl ==
 	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
 		if (__ccw_device_should_retry(&irb->scsw))
 			__ccw_device_disband_start(cdev);
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 4a38993..1b03c54 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -29,9 +29,11 @@
 static void
 ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb)
 {
-	if (!(irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK |
-				 SCHN_STAT_CHN_CTRL_CHK |
-				 SCHN_STAT_INTF_CTRL_CHK)))
+	char dbf_text[15];
+
+	if (!scsw_is_valid_cstat(&irb->scsw) ||
+	    !(scsw_cstat(&irb->scsw) & (SCHN_STAT_CHN_DATA_CHK |
+	      SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK)))
 		return;
 	CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check "
 		      "received"
@@ -39,15 +41,10 @@
 		      ": %02X sch_stat : %02X\n",
 		      cdev->private->dev_id.devno, cdev->private->schid.ssid,
 		      cdev->private->schid.sch_no,
-		      irb->scsw.dstat, irb->scsw.cstat);
-
-	if (irb->scsw.cc != 3) {
-		char dbf_text[15];
-
-		sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no);
-		CIO_TRACE_EVENT(0, dbf_text);
-		CIO_HEX_EVENT(0, irb, sizeof (struct irb));
-	}
+		      scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw));
+	sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no);
+	CIO_TRACE_EVENT(0, dbf_text);
+	CIO_HEX_EVENT(0, irb, sizeof(struct irb));
 }
 
 /*
@@ -81,12 +78,12 @@
 	 * are condition that have to be met for the extended control
 	 * bit to have meaning. Sick.
 	 */
-	cdev->private->irb.scsw.ectl = 0;
-	if ((irb->scsw.stctl & SCSW_STCTL_ALERT_STATUS) &&
-	    !(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS))
-		cdev->private->irb.scsw.ectl = irb->scsw.ectl;
+	cdev->private->irb.scsw.cmd.ectl = 0;
+	if ((irb->scsw.cmd.stctl & SCSW_STCTL_ALERT_STATUS) &&
+	    !(irb->scsw.cmd.stctl & SCSW_STCTL_INTER_STATUS))
+		cdev->private->irb.scsw.cmd.ectl = irb->scsw.cmd.ectl;
 	/* Check if extended control word is valid. */
-	if (!cdev->private->irb.scsw.ectl)
+	if (!cdev->private->irb.scsw.cmd.ectl)
 		return;
 	/* Copy concurrent sense / model dependent information. */
 	memcpy (&cdev->private->irb.ecw, irb->ecw, sizeof (irb->ecw));
@@ -98,11 +95,12 @@
 static int
 ccw_device_accumulate_esw_valid(struct irb *irb)
 {
-	if (!irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND)
+	if (!irb->scsw.cmd.eswf &&
+	    (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND))
 		return 0;
-	if (irb->scsw.stctl == 
-	    		(SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
-	    !(irb->scsw.actl & SCSW_ACTL_SUSPENDED))
+	if (irb->scsw.cmd.stctl ==
+			(SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
+	    !(irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
 		return 0;
 	return 1;
 }
@@ -125,7 +123,7 @@
 	cdev_irb->esw.esw1.lpum = irb->esw.esw1.lpum;
 
 	/* Copy subchannel logout information if esw is of format 0. */
-	if (irb->scsw.eswf) {
+	if (irb->scsw.cmd.eswf) {
 		cdev_sublog = &cdev_irb->esw.esw0.sublog;
 		sublog = &irb->esw.esw0.sublog;
 		/* Copy extended status flags. */
@@ -134,7 +132,7 @@
 		 * Copy fields that have a meaning for channel data check
 		 * channel control check and interface control check.
 		 */
-		if (irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK |
+		if (irb->scsw.cmd.cstat & (SCHN_STAT_CHN_DATA_CHK |
 				       SCHN_STAT_CHN_CTRL_CHK |
 				       SCHN_STAT_INTF_CTRL_CHK)) {
 			/* Copy ancillary report bit. */
@@ -155,7 +153,7 @@
 		/* Copy i/o-error alert. */
 		cdev_sublog->ioerr = sublog->ioerr;
 		/* Copy channel path timeout bit. */
-		if (irb->scsw.cstat & SCHN_STAT_INTF_CTRL_CHK)
+		if (irb->scsw.cmd.cstat & SCHN_STAT_INTF_CTRL_CHK)
 			cdev_irb->esw.esw0.erw.cpt = irb->esw.esw0.erw.cpt;
 		/* Copy failing storage address validity flag. */
 		cdev_irb->esw.esw0.erw.fsavf = irb->esw.esw0.erw.fsavf;
@@ -200,24 +198,24 @@
 	 * If not, the remaining bit have no meaning and we must ignore them.
 	 * The esw is not meaningful as well...
 	 */
-	if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND))
+	if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))
 		return;
 
 	/* Check for channel checks and interface control checks. */
 	ccw_device_msg_control_check(cdev, irb);
 
 	/* Check for path not operational. */
-	if (irb->scsw.pno && irb->scsw.fctl != 0 &&
-	    (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) ||
-	     (irb->scsw.actl & SCSW_ACTL_SUSPENDED)))
+	if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw))
 		ccw_device_path_notoper(cdev);
-
+	/* No irb accumulation for transport mode irbs. */
+	if (scsw_is_tm(&irb->scsw)) {
+		memcpy(&cdev->private->irb, irb, sizeof(struct irb));
+		return;
+	}
 	/*
 	 * Don't accumulate unsolicited interrupts.
 	 */
-	if ((irb->scsw.stctl ==
-	     (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
-	    (!irb->scsw.cc))
+	if (!scsw_is_solicited(&irb->scsw))
 		return;
 
 	cdev_irb = &cdev->private->irb;
@@ -227,62 +225,63 @@
 	 * status at the subchannel has been cleared and we must not pass
 	 * intermediate accumulated status to the device driver.
 	 */
-	if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC)
+	if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
 		memset(&cdev->private->irb, 0, sizeof(struct irb));
 
 	/* Copy bits which are valid only for the start function. */
-	if (irb->scsw.fctl & SCSW_FCTL_START_FUNC) {
+	if (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) {
 		/* Copy key. */
-		cdev_irb->scsw.key = irb->scsw.key;
+		cdev_irb->scsw.cmd.key = irb->scsw.cmd.key;
 		/* Copy suspend control bit. */
-		cdev_irb->scsw.sctl = irb->scsw.sctl;
+		cdev_irb->scsw.cmd.sctl = irb->scsw.cmd.sctl;
 		/* Accumulate deferred condition code. */
-		cdev_irb->scsw.cc |= irb->scsw.cc;
+		cdev_irb->scsw.cmd.cc |= irb->scsw.cmd.cc;
 		/* Copy ccw format bit. */
-		cdev_irb->scsw.fmt = irb->scsw.fmt;
+		cdev_irb->scsw.cmd.fmt = irb->scsw.cmd.fmt;
 		/* Copy prefetch bit. */
-		cdev_irb->scsw.pfch = irb->scsw.pfch;
+		cdev_irb->scsw.cmd.pfch = irb->scsw.cmd.pfch;
 		/* Copy initial-status-interruption-control. */
-		cdev_irb->scsw.isic = irb->scsw.isic;
+		cdev_irb->scsw.cmd.isic = irb->scsw.cmd.isic;
 		/* Copy address limit checking control. */
-		cdev_irb->scsw.alcc = irb->scsw.alcc;
+		cdev_irb->scsw.cmd.alcc = irb->scsw.cmd.alcc;
 		/* Copy suppress suspend bit. */
-		cdev_irb->scsw.ssi = irb->scsw.ssi;
+		cdev_irb->scsw.cmd.ssi = irb->scsw.cmd.ssi;
 	}
 
 	/* Take care of the extended control bit and extended control word. */
 	ccw_device_accumulate_ecw(cdev, irb);
 	    
 	/* Accumulate function control. */
-	cdev_irb->scsw.fctl |= irb->scsw.fctl;
+	cdev_irb->scsw.cmd.fctl |= irb->scsw.cmd.fctl;
 	/* Copy activity control. */
-	cdev_irb->scsw.actl= irb->scsw.actl;
+	cdev_irb->scsw.cmd.actl = irb->scsw.cmd.actl;
 	/* Accumulate status control. */
-	cdev_irb->scsw.stctl |= irb->scsw.stctl;
+	cdev_irb->scsw.cmd.stctl |= irb->scsw.cmd.stctl;
 	/*
 	 * Copy ccw address if it is valid. This is a bit simplified
 	 * but should be close enough for all practical purposes.
 	 */
-	if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) ||
-	    ((irb->scsw.stctl == 
+	if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) ||
+	    ((irb->scsw.cmd.stctl ==
 	      (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND)) &&
-	     (irb->scsw.actl & SCSW_ACTL_DEVACT) &&
-	     (irb->scsw.actl & SCSW_ACTL_SCHACT)) ||
-	    (irb->scsw.actl & SCSW_ACTL_SUSPENDED))
-		cdev_irb->scsw.cpa = irb->scsw.cpa;
+	     (irb->scsw.cmd.actl & SCSW_ACTL_DEVACT) &&
+	     (irb->scsw.cmd.actl & SCSW_ACTL_SCHACT)) ||
+	    (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
+		cdev_irb->scsw.cmd.cpa = irb->scsw.cmd.cpa;
 	/* Accumulate device status, but not the device busy flag. */
-	cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY;
+	cdev_irb->scsw.cmd.dstat &= ~DEV_STAT_BUSY;
 	/* dstat is not always valid. */
-	if (irb->scsw.stctl &
+	if (irb->scsw.cmd.stctl &
 	    (SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_SEC_STATUS
 	     | SCSW_STCTL_INTER_STATUS | SCSW_STCTL_ALERT_STATUS))
-		cdev_irb->scsw.dstat |= irb->scsw.dstat;
+		cdev_irb->scsw.cmd.dstat |= irb->scsw.cmd.dstat;
 	/* Accumulate subchannel status. */
-	cdev_irb->scsw.cstat |= irb->scsw.cstat;
+	cdev_irb->scsw.cmd.cstat |= irb->scsw.cmd.cstat;
 	/* Copy residual count if it is valid. */
-	if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
-	    (irb->scsw.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN)) == 0)
-		cdev_irb->scsw.count = irb->scsw.count;
+	if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) &&
+	    (irb->scsw.cmd.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN))
+	     == 0)
+		cdev_irb->scsw.cmd.count = irb->scsw.cmd.count;
 
 	/* Take care of bits in the extended status word. */
 	ccw_device_accumulate_esw(cdev, irb);
@@ -299,7 +298,7 @@
 	 *	 sense facility available/supported when enabling the
 	 *	 concurrent sense facility.
 	 */
-	if ((cdev_irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+	if ((cdev_irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
 	    !(cdev_irb->esw.esw0.erw.cons))
 		cdev->private->flags.dosense = 1;
 }
@@ -317,7 +316,7 @@
 	sch = to_subchannel(cdev->dev.parent);
 
 	/* A sense is required, can we do it now ? */
-	if ((irb->scsw.actl  & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
+	if (scsw_actl(&irb->scsw) & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT))
 		/*
 		 * we received an Unit Check but we have no final
 		 *  status yet, therefore we must delay the SENSE
@@ -355,20 +354,18 @@
 	 * If not, the remaining bit have no meaning and we must ignore them.
 	 * The esw is not meaningful as well...
 	 */
-	if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND))
+	if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))
 		return;
 
 	/* Check for channel checks and interface control checks. */
 	ccw_device_msg_control_check(cdev, irb);
 
 	/* Check for path not operational. */
-	if (irb->scsw.pno && irb->scsw.fctl != 0 &&
-	    (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) ||
-	     (irb->scsw.actl & SCSW_ACTL_SUSPENDED)))
+	if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw))
 		ccw_device_path_notoper(cdev);
 
-	if (!(irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
-	    (irb->scsw.dstat & DEV_STAT_CHN_END)) {
+	if (!(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
+	    (irb->scsw.cmd.dstat & DEV_STAT_CHN_END)) {
 		cdev->private->irb.esw.esw0.erw.cons = 1;
 		cdev->private->flags.dosense = 0;
 	}
@@ -386,11 +383,11 @@
 ccw_device_accumulate_and_sense(struct ccw_device *cdev, struct irb *irb)
 {
 	ccw_device_accumulate_irb(cdev, irb);
-	if ((irb->scsw.actl  & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
+	if ((irb->scsw.cmd.actl  & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
 		return -EBUSY;
 	/* Check for basic sense. */
 	if (cdev->private->flags.dosense &&
-	    !(irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) {
+	    !(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)) {
 		cdev->private->irb.esw.esw0.erw.cons = 1;
 		cdev->private->flags.dosense = 0;
 		return 0;
diff --git a/drivers/s390/cio/fcx.c b/drivers/s390/cio/fcx.c
new file mode 100644
index 0000000..61677df
--- /dev/null
+++ b/drivers/s390/cio/fcx.c
@@ -0,0 +1,350 @@
+/*
+ *  Functions for assembling fcx enabled I/O control blocks.
+ *
+ *    Copyright IBM Corp. 2008
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/fcx.h>
+#include "cio.h"
+
+/**
+ * tcw_get_intrg - return pointer to associated interrogate tcw
+ * @tcw: pointer to the original tcw
+ *
+ * Return a pointer to the interrogate tcw associated with the specified tcw
+ * or %NULL if there is no associated interrogate tcw.
+ */
+struct tcw *tcw_get_intrg(struct tcw *tcw)
+{
+	return (struct tcw *) ((addr_t) tcw->intrg);
+}
+EXPORT_SYMBOL(tcw_get_intrg);
+
+/**
+ * tcw_get_data - return pointer to input/output data associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return the input or output data address specified in the tcw depending
+ * on whether the r-bit or the w-bit is set. If neither bit is set, return
+ * %NULL.
+ */
+void *tcw_get_data(struct tcw *tcw)
+{
+	if (tcw->r)
+		return (void *) ((addr_t) tcw->input);
+	if (tcw->w)
+		return (void *) ((addr_t) tcw->output);
+	return NULL;
+}
+EXPORT_SYMBOL(tcw_get_data);
+
+/**
+ * tcw_get_tccb - return pointer to tccb associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return pointer to the tccb associated with this tcw.
+ */
+struct tccb *tcw_get_tccb(struct tcw *tcw)
+{
+	return (struct tccb *) ((addr_t) tcw->tccb);
+}
+EXPORT_SYMBOL(tcw_get_tccb);
+
+/**
+ * tcw_get_tsb - return pointer to tsb associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return pointer to the tsb associated with this tcw.
+ */
+struct tsb *tcw_get_tsb(struct tcw *tcw)
+{
+	return (struct tsb *) ((addr_t) tcw->tsb);
+}
+EXPORT_SYMBOL(tcw_get_tsb);
+
+/**
+ * tcw_init - initialize tcw data structure
+ * @tcw: pointer to the tcw to be initialized
+ * @r: initial value of the r-bit
+ * @w: initial value of the w-bit
+ *
+ * Initialize all fields of the specified tcw data structure with zero and
+ * fill in the format, flags, r and w fields.
+ */
+void tcw_init(struct tcw *tcw, int r, int w)
+{
+	memset(tcw, 0, sizeof(struct tcw));
+	tcw->format = TCW_FORMAT_DEFAULT;
+	tcw->flags = TCW_FLAGS_TIDAW_FORMAT(TCW_TIDAW_FORMAT_DEFAULT);
+	if (r)
+		tcw->r = 1;
+	if (w)
+		tcw->w = 1;
+}
+EXPORT_SYMBOL(tcw_init);
+
+static inline size_t tca_size(struct tccb *tccb)
+{
+	return tccb->tcah.tcal - 12;
+}
+
+static u32 calc_dcw_count(struct tccb *tccb)
+{
+	int offset;
+	struct dcw *dcw;
+	u32 count = 0;
+	size_t size;
+
+	size = tca_size(tccb);
+	for (offset = 0; offset < size;) {
+		dcw = (struct dcw *) &tccb->tca[offset];
+		count += dcw->count;
+		if (!(dcw->flags & DCW_FLAGS_CC))
+			break;
+		offset += sizeof(struct dcw) + ALIGN((int) dcw->cd_count, 4);
+	}
+	return count;
+}
+
+static u32 calc_cbc_size(struct tidaw *tidaw, int num)
+{
+	int i;
+	u32 cbc_data;
+	u32 cbc_count = 0;
+	u64 data_count = 0;
+
+	for (i = 0; i < num; i++) {
+		if (tidaw[i].flags & TIDAW_FLAGS_LAST)
+			break;
+		/* TODO: find out if padding applies to total of data
+		 * transferred or data transferred by this tidaw. Assumption:
+		 * applies to total. */
+		data_count += tidaw[i].count;
+		if (tidaw[i].flags & TIDAW_FLAGS_INSERT_CBC) {
+			cbc_data = 4 + ALIGN(data_count, 4) - data_count;
+			cbc_count += cbc_data;
+			data_count += cbc_data;
+		}
+	}
+	return cbc_count;
+}
+
+/**
+ * tcw_finalize - finalize tcw length fields and tidaw list
+ * @tcw: pointer to the tcw
+ * @num_tidaws: the number of tidaws used to address input/output data or zero
+ * if no tida is used
+ *
+ * Calculate the input-/output-count and tccbl field in the tcw, add a
+ * tcat the tccb and terminate the data tidaw list if used.
+ *
+ * Note: in case input- or output-tida is used, the tidaw-list must be stored
+ * in contiguous storage (no ttic). The tcal field in the tccb must be
+ * up-to-date.
+ */
+void tcw_finalize(struct tcw *tcw, int num_tidaws)
+{
+	struct tidaw *tidaw;
+	struct tccb *tccb;
+	struct tccb_tcat *tcat;
+	u32 count;
+
+	/* Terminate tidaw list. */
+	tidaw = tcw_get_data(tcw);
+	if (num_tidaws > 0)
+		tidaw[num_tidaws - 1].flags |= TIDAW_FLAGS_LAST;
+	/* Add tcat to tccb. */
+	tccb = tcw_get_tccb(tcw);
+	tcat = (struct tccb_tcat *) &tccb->tca[tca_size(tccb)];
+	memset(tcat, 0, sizeof(tcat));
+	/* Calculate tcw input/output count and tcat transport count. */
+	count = calc_dcw_count(tccb);
+	if (tcw->w && (tcw->flags & TCW_FLAGS_OUTPUT_TIDA))
+		count += calc_cbc_size(tidaw, num_tidaws);
+	if (tcw->r)
+		tcw->input_count = count;
+	else if (tcw->w)
+		tcw->output_count = count;
+	tcat->count = ALIGN(count, 4) + 4;
+	/* Calculate tccbl. */
+	tcw->tccbl = (sizeof(struct tccb) + tca_size(tccb) +
+		      sizeof(struct tccb_tcat) - 20) >> 2;
+}
+EXPORT_SYMBOL(tcw_finalize);
+
+/**
+ * tcw_set_intrg - set the interrogate tcw address of a tcw
+ * @tcw: the tcw address
+ * @intrg_tcw: the address of the interrogate tcw
+ *
+ * Set the address of the interrogate tcw in the specified tcw.
+ */
+void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw)
+{
+	tcw->intrg = (u32) ((addr_t) intrg_tcw);
+}
+EXPORT_SYMBOL(tcw_set_intrg);
+
+/**
+ * tcw_set_data - set data address and tida flag of a tcw
+ * @tcw: the tcw address
+ * @data: the data address
+ * @use_tidal: zero of the data address specifies a contiguous block of data,
+ * non-zero if it specifies a list if tidaws.
+ *
+ * Set the input/output data address of a tcw (depending on the value of the
+ * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
+ * is set as well.
+ */
+void tcw_set_data(struct tcw *tcw, void *data, int use_tidal)
+{
+	if (tcw->r) {
+		tcw->input = (u64) ((addr_t) data);
+		if (use_tidal)
+			tcw->flags |= TCW_FLAGS_INPUT_TIDA;
+	} else if (tcw->w) {
+		tcw->output = (u64) ((addr_t) data);
+		if (use_tidal)
+			tcw->flags |= TCW_FLAGS_OUTPUT_TIDA;
+	}
+}
+EXPORT_SYMBOL(tcw_set_data);
+
+/**
+ * tcw_set_tccb - set tccb address of a tcw
+ * @tcw: the tcw address
+ * @tccb: the tccb address
+ *
+ * Set the address of the tccb in the specified tcw.
+ */
+void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb)
+{
+	tcw->tccb = (u64) ((addr_t) tccb);
+}
+EXPORT_SYMBOL(tcw_set_tccb);
+
+/**
+ * tcw_set_tsb - set tsb address of a tcw
+ * @tcw: the tcw address
+ * @tsb: the tsb address
+ *
+ * Set the address of the tsb in the specified tcw.
+ */
+void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb)
+{
+	tcw->tsb = (u64) ((addr_t) tsb);
+}
+EXPORT_SYMBOL(tcw_set_tsb);
+
+/**
+ * tccb_init - initialize tccb
+ * @tccb: the tccb address
+ * @size: the maximum size of the tccb
+ * @sac: the service-action-code to be user
+ *
+ * Initialize the header of the specified tccb by resetting all values to zero
+ * and filling in defaults for format, sac and initial tcal fields.
+ */
+void tccb_init(struct tccb *tccb, size_t size, u32 sac)
+{
+	memset(tccb, 0, size);
+	tccb->tcah.format = TCCB_FORMAT_DEFAULT;
+	tccb->tcah.sac = sac;
+	tccb->tcah.tcal = 12;
+}
+EXPORT_SYMBOL(tccb_init);
+
+/**
+ * tsb_init - initialize tsb
+ * @tsb: the tsb address
+ *
+ * Initialize the specified tsb by resetting all values to zero.
+ */
+void tsb_init(struct tsb *tsb)
+{
+	memset(tsb, 0, sizeof(tsb));
+}
+EXPORT_SYMBOL(tsb_init);
+
+/**
+ * tccb_add_dcw - add a dcw to the tccb
+ * @tccb: the tccb address
+ * @tccb_size: the maximum tccb size
+ * @cmd: the dcw command
+ * @flags: flags for the dcw
+ * @cd: pointer to control data for this dcw or NULL if none is required
+ * @cd_count: number of control data bytes for this dcw
+ * @count: number of data bytes for this dcw
+ *
+ * Add a new dcw to the specified tccb by writing the dcw information specified
+ * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
+ * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
+ * would exceed the available space as defined by @tccb_size.
+ *
+ * Note: the tcal field of the tccb header will be updates to reflect added
+ * content.
+ */
+struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags,
+			 void *cd, u8 cd_count, u32 count)
+{
+	struct dcw *dcw;
+	int size;
+	int tca_offset;
+
+	/* Check for space. */
+	tca_offset = tca_size(tccb);
+	size = ALIGN(sizeof(struct dcw) + cd_count, 4);
+	if (sizeof(struct tccb_tcah) + tca_offset + size +
+	    sizeof(struct tccb_tcat) > tccb_size)
+		return ERR_PTR(-ENOSPC);
+	/* Add dcw to tca. */
+	dcw = (struct dcw *) &tccb->tca[tca_offset];
+	memset(dcw, 0, size);
+	dcw->cmd = cmd;
+	dcw->flags = flags;
+	dcw->count = count;
+	dcw->cd_count = cd_count;
+	if (cd)
+		memcpy(&dcw->cd[0], cd, cd_count);
+	tccb->tcah.tcal += size;
+	return dcw;
+}
+EXPORT_SYMBOL(tccb_add_dcw);
+
+/**
+ * tcw_add_tidaw - add a tidaw to a tcw
+ * @tcw: the tcw address
+ * @num_tidaws: the current number of tidaws
+ * @flags: flags for the new tidaw
+ * @addr: address value for the new tidaw
+ * @count: count value for the new tidaw
+ *
+ * Add a new tidaw to the input/output data tidaw-list of the specified tcw
+ * (depending on the value of the r-flag and w-flag) and return a pointer to
+ * the new tidaw.
+ *
+ * Note: the tidaw-list is assumed to be contiguous with no ttics. The caller
+ * must ensure that there is enough space for the new tidaw. The last-tidaw
+ * flag for the last tidaw in the list will be set by tcw_finalize.
+ */
+struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags,
+			    void *addr, u32 count)
+{
+	struct tidaw *tidaw;
+
+	/* Add tidaw to tidaw-list. */
+	tidaw = ((struct tidaw *) tcw_get_data(tcw)) + num_tidaws;
+	memset(tidaw, 0, sizeof(struct tidaw));
+	tidaw->flags = flags;
+	tidaw->count = count;
+	tidaw->addr = (u64) ((addr_t) addr);
+	return tidaw;
+}
+EXPORT_SYMBOL(tcw_add_tidaw);
diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h
index 144466a..528065c 100644
--- a/drivers/s390/cio/idset.h
+++ b/drivers/s390/cio/idset.h
@@ -8,7 +8,7 @@
 #ifndef S390_IDSET_H
 #define S390_IDSET_H S390_IDSET_H
 
-#include "schid.h"
+#include <asm/schid.h>
 
 struct idset;
 
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 8c61316..3f8f1cf 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -1,12 +1,12 @@
 #ifndef S390_IO_SCH_H
 #define S390_IO_SCH_H
 
-#include "schid.h"
+#include <asm/schid.h>
 
 /*
- * operation request block
+ * command-mode operation request block
  */
-struct orb {
+struct cmd_orb {
 	u32 intparm;	/* interruption parameter */
 	u32 key  : 4;	/* flags, like key, suspend control, etc. */
 	u32 spnd : 1;	/* suspend control */
@@ -28,8 +28,36 @@
 	u32 cpa;	/* channel program address */
 }  __attribute__ ((packed, aligned(4)));
 
+/*
+ * transport-mode operation request block
+ */
+struct tm_orb {
+	u32 intparm;
+	u32 key:4;
+	u32 :9;
+	u32 b:1;
+	u32 :2;
+	u32 lpm:8;
+	u32 :7;
+	u32 x:1;
+	u32 tcw;
+	u32 prio:8;
+	u32 :8;
+	u32 rsvpgm:8;
+	u32 :8;
+	u32 :32;
+	u32 :32;
+	u32 :32;
+	u32 :32;
+}  __attribute__ ((packed, aligned(4)));
+
+union orb {
+	struct cmd_orb cmd;
+	struct tm_orb tm;
+}  __attribute__ ((packed, aligned(4)));
+
 struct io_subchannel_private {
-	struct orb orb;		/* operation request block */
+	union orb orb;		/* operation request block */
 	struct ccw1 sense_ccw;	/* static ccw for sense command */
 } __attribute__ ((aligned(8)));
 
@@ -95,16 +123,18 @@
 	void *cmb_wait;			/* deferred cmb enable/disable */
 };
 
-static inline int ssch(struct subchannel_id schid, volatile struct orb *addr)
+static inline int ssch(struct subchannel_id schid, volatile union orb *addr)
 {
 	register struct subchannel_id reg1 asm("1") = schid;
-	int ccode;
+	int ccode = -EIO;
 
 	asm volatile(
 		"	ssch	0(%2)\n"
-		"	ipm	%0\n"
-		"	srl	%0,28"
-		: "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+		"0:	ipm	%0\n"
+		"	srl	%0,28\n"
+		"1:\n"
+		EX_TABLE(0b, 1b)
+		: "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
 	return ccode;
 }
 
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 652ea36..9fa2ac1 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -2,7 +2,7 @@
 #define S390_CIO_IOASM_H
 
 #include <asm/chpid.h>
-#include "schid.h"
+#include <asm/schid.h>
 
 /*
  * TPI info structure
diff --git a/drivers/s390/cio/isc.c b/drivers/s390/cio/isc.c
new file mode 100644
index 0000000..c592087
--- /dev/null
+++ b/drivers/s390/cio/isc.c
@@ -0,0 +1,68 @@
+/*
+ * Functions for registration of I/O interruption subclasses on s390.
+ *
+ * Copyright IBM Corp. 2008
+ * Authors: Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <asm/isc.h>
+
+static unsigned int isc_refs[MAX_ISC + 1];
+static DEFINE_SPINLOCK(isc_ref_lock);
+
+
+/**
+ * isc_register - register an I/O interruption subclass.
+ * @isc: I/O interruption subclass to register
+ *
+ * The number of users for @isc is increased. If this is the first user to
+ * register @isc, the corresponding I/O interruption subclass mask is enabled.
+ *
+ * Context:
+ *   This function must not be called in interrupt context.
+ */
+void isc_register(unsigned int isc)
+{
+	if (isc > MAX_ISC) {
+		WARN_ON(1);
+		return;
+	}
+
+	spin_lock(&isc_ref_lock);
+	if (isc_refs[isc] == 0)
+		ctl_set_bit(6, 31 - isc);
+	isc_refs[isc]++;
+	spin_unlock(&isc_ref_lock);
+}
+EXPORT_SYMBOL_GPL(isc_register);
+
+/**
+ * isc_unregister - unregister an I/O interruption subclass.
+ * @isc: I/O interruption subclass to unregister
+ *
+ * The number of users for @isc is decreased. If this is the last user to
+ * unregister @isc, the corresponding I/O interruption subclass mask is
+ * disabled.
+ * Note: This function must not be called if isc_register() hasn't been called
+ * before by the driver for @isc.
+ *
+ * Context:
+ *   This function must not be called in interrupt context.
+ */
+void isc_unregister(unsigned int isc)
+{
+	spin_lock(&isc_ref_lock);
+	/* check for misuse */
+	if (isc > MAX_ISC || isc_refs[isc] == 0) {
+		WARN_ON(1);
+		goto out_unlock;
+	}
+	if (isc_refs[isc] == 1)
+		ctl_clear_bit(6, 31 - isc);
+	isc_refs[isc]--;
+out_unlock:
+	spin_unlock(&isc_ref_lock);
+}
+EXPORT_SYMBOL_GPL(isc_unregister);
diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c
new file mode 100644
index 0000000..17da9ab
--- /dev/null
+++ b/drivers/s390/cio/itcw.c
@@ -0,0 +1,327 @@
+/*
+ *  Functions for incremental construction of fcx enabled I/O control blocks.
+ *
+ *    Copyright IBM Corp. 2008
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/fcx.h>
+#include <asm/itcw.h>
+
+/**
+ * struct itcw - incremental tcw helper data type
+ *
+ * This structure serves as a handle for the incremental construction of a
+ * tcw and associated tccb, tsb, data tidaw-list plus an optional interrogate
+ * tcw and associated data. The data structures are contained inside a single
+ * contiguous buffer provided by the user.
+ *
+ * The itcw construction functions take care of overall data integrity:
+ * - reset unused fields to zero
+ * - fill in required pointers
+ * - ensure required alignment for data structures
+ * - prevent data structures to cross 4k-byte boundary where required
+ * - calculate tccb-related length fields
+ * - optionally provide ready-made interrogate tcw and associated structures
+ *
+ * Restrictions apply to the itcws created with these construction functions:
+ * - tida only supported for data address, not for tccb
+ * - only contiguous tidaw-lists (no ttic)
+ * - total number of bytes required per itcw may not exceed 4k bytes
+ * - either read or write operation (may not work with r=0 and w=0)
+ *
+ * Example:
+ * struct itcw *itcw;
+ * void *buffer;
+ * size_t size;
+ *
+ * size = itcw_calc_size(1, 2, 0);
+ * buffer = kmalloc(size, GFP_DMA);
+ * if (!buffer)
+ *	return -ENOMEM;
+ * itcw = itcw_init(buffer, size, ITCW_OP_READ, 1, 2, 0);
+ * if (IS_ERR(itcw))
+ *	return PTR_ER(itcw);
+ * itcw_add_dcw(itcw, 0x2, 0, NULL, 0, 72);
+ * itcw_add_tidaw(itcw, 0, 0x30000, 20);
+ * itcw_add_tidaw(itcw, 0, 0x40000, 52);
+ * itcw_finalize(itcw);
+ *
+ */
+struct itcw {
+	struct tcw *tcw;
+	struct tcw *intrg_tcw;
+	int num_tidaws;
+	int max_tidaws;
+	int intrg_num_tidaws;
+	int intrg_max_tidaws;
+};
+
+/**
+ * itcw_get_tcw - return pointer to tcw associated with the itcw
+ * @itcw: address of the itcw
+ *
+ * Return pointer to the tcw associated with the itcw.
+ */
+struct tcw *itcw_get_tcw(struct itcw *itcw)
+{
+	return itcw->tcw;
+}
+EXPORT_SYMBOL(itcw_get_tcw);
+
+/**
+ * itcw_calc_size - return the size of an itcw with the given parameters
+ * @intrg: if non-zero, add an interrogate tcw
+ * @max_tidaws: maximum number of tidaws to be used for data addressing or zero
+ * if no tida is to be used.
+ * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing
+ * by the interrogate tcw, if specified
+ *
+ * Calculate and return the number of bytes required to hold an itcw with the
+ * given parameters and assuming tccbs with maximum size.
+ *
+ * Note that the resulting size also contains bytes needed for alignment
+ * padding as well as padding to ensure that data structures don't cross a
+ * 4k-boundary where required.
+ */
+size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws)
+{
+	size_t len;
+
+	/* Main data. */
+	len = sizeof(struct itcw);
+	len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
+	       /* TSB */ sizeof(struct tsb) +
+	       /* TIDAL */ max_tidaws * sizeof(struct tidaw);
+	/* Interrogate data. */
+	if (intrg) {
+		len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
+		       /* TSB */ sizeof(struct tsb) +
+		       /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw);
+	}
+	/* Maximum required alignment padding. */
+	len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7;
+	/* Maximum padding for structures that may not cross 4k boundary. */
+	if ((max_tidaws > 0) || (intrg_max_tidaws > 0))
+		len += max(max_tidaws, intrg_max_tidaws) *
+		       sizeof(struct tidaw) - 1;
+	return len;
+}
+EXPORT_SYMBOL(itcw_calc_size);
+
+#define CROSS4K(x, l)	(((x) & ~4095) != ((x + l) & ~4095))
+
+static inline void *fit_chunk(addr_t *start, addr_t end, size_t len,
+			      int align, int check_4k)
+{
+	addr_t addr;
+
+	addr = ALIGN(*start, align);
+	if (check_4k && CROSS4K(addr, len)) {
+		addr = ALIGN(addr, 4096);
+		addr = ALIGN(addr, align);
+	}
+	if (addr + len > end)
+		return ERR_PTR(-ENOSPC);
+	*start = addr + len;
+	return (void *) addr;
+}
+
+/**
+ * itcw_init - initialize incremental tcw data structure
+ * @buffer: address of buffer to use for data structures
+ * @size: number of bytes in buffer
+ * @op: %ITCW_OP_READ for a read operation tcw, %ITCW_OP_WRITE for a write
+ * operation tcw
+ * @intrg: if non-zero, add and initialize an interrogate tcw
+ * @max_tidaws: maximum number of tidaws to be used for data addressing or zero
+ * if no tida is to be used.
+ * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing
+ * by the interrogate tcw, if specified
+ *
+ * Prepare the specified buffer to be used as an incremental tcw, i.e. a
+ * helper data structure that can be used to construct a valid tcw by
+ * successive calls to other helper functions. Note: the buffer needs to be
+ * located below the 2G address limit. The resulting tcw has the following
+ * restrictions:
+ *  - no tccb tidal
+ *  - input/output tidal is contiguous (no ttic)
+ *  - total data should not exceed 4k
+ *  - tcw specifies either read or write operation
+ *
+ * On success, return pointer to the resulting incremental tcw data structure,
+ * ERR_PTR otherwise.
+ */
+struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
+		       int max_tidaws, int intrg_max_tidaws)
+{
+	struct itcw *itcw;
+	void *chunk;
+	addr_t start;
+	addr_t end;
+
+	/* Check for 2G limit. */
+	start = (addr_t) buffer;
+	end = start + size;
+	if (end > (1 << 31))
+		return ERR_PTR(-EINVAL);
+	memset(buffer, 0, size);
+	/* ITCW. */
+	chunk = fit_chunk(&start, end, sizeof(struct itcw), 1, 0);
+	if (IS_ERR(chunk))
+		return chunk;
+	itcw = chunk;
+	itcw->max_tidaws = max_tidaws;
+	itcw->intrg_max_tidaws = intrg_max_tidaws;
+	/* Main TCW. */
+	chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
+	if (IS_ERR(chunk))
+		return chunk;
+	itcw->tcw = chunk;
+	tcw_init(itcw->tcw, (op == ITCW_OP_READ) ? 1 : 0,
+		 (op == ITCW_OP_WRITE) ? 1 : 0);
+	/* Interrogate TCW. */
+	if (intrg) {
+		chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
+		if (IS_ERR(chunk))
+			return chunk;
+		itcw->intrg_tcw = chunk;
+		tcw_init(itcw->intrg_tcw, 1, 0);
+		tcw_set_intrg(itcw->tcw, itcw->intrg_tcw);
+	}
+	/* Data TIDAL. */
+	if (max_tidaws > 0) {
+		chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
+				  max_tidaws, 16, 1);
+		if (IS_ERR(chunk))
+			return chunk;
+		tcw_set_data(itcw->tcw, chunk, 1);
+	}
+	/* Interrogate data TIDAL. */
+	if (intrg && (intrg_max_tidaws > 0)) {
+		chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
+				  intrg_max_tidaws, 16, 1);
+		if (IS_ERR(chunk))
+			return chunk;
+		tcw_set_data(itcw->intrg_tcw, chunk, 1);
+	}
+	/* TSB. */
+	chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
+	if (IS_ERR(chunk))
+		return chunk;
+	tsb_init(chunk);
+	tcw_set_tsb(itcw->tcw, chunk);
+	/* Interrogate TSB. */
+	if (intrg) {
+		chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
+		if (IS_ERR(chunk))
+			return chunk;
+		tsb_init(chunk);
+		tcw_set_tsb(itcw->intrg_tcw, chunk);
+	}
+	/* TCCB. */
+	chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
+	if (IS_ERR(chunk))
+		return chunk;
+	tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_DEFAULT);
+	tcw_set_tccb(itcw->tcw, chunk);
+	/* Interrogate TCCB. */
+	if (intrg) {
+		chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
+		if (IS_ERR(chunk))
+			return chunk;
+		tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_INTRG);
+		tcw_set_tccb(itcw->intrg_tcw, chunk);
+		tccb_add_dcw(chunk, TCCB_MAX_SIZE, DCW_CMD_INTRG, 0, NULL,
+			     sizeof(struct dcw_intrg_data), 0);
+		tcw_finalize(itcw->intrg_tcw, 0);
+	}
+	return itcw;
+}
+EXPORT_SYMBOL(itcw_init);
+
+/**
+ * itcw_add_dcw - add a dcw to the itcw
+ * @itcw: address of the itcw
+ * @cmd: the dcw command
+ * @flags: flags for the dcw
+ * @cd: address of control data for this dcw or NULL if none is required
+ * @cd_count: number of control data bytes for this dcw
+ * @count: number of data bytes for this dcw
+ *
+ * Add a new dcw to the specified itcw by writing the dcw information specified
+ * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
+ * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
+ * would exceed the available space.
+ *
+ * Note: the tcal field of the tccb header will be updated to reflect added
+ * content.
+ */
+struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd,
+			 u8 cd_count, u32 count)
+{
+	return tccb_add_dcw(tcw_get_tccb(itcw->tcw), TCCB_MAX_SIZE, cmd,
+			    flags, cd, cd_count, count);
+}
+EXPORT_SYMBOL(itcw_add_dcw);
+
+/**
+ * itcw_add_tidaw - add a tidaw to the itcw
+ * @itcw: address of the itcw
+ * @flags: flags for the new tidaw
+ * @addr: address value for the new tidaw
+ * @count: count value for the new tidaw
+ *
+ * Add a new tidaw to the input/output data tidaw-list of the specified itcw
+ * (depending on the value of the r-flag and w-flag). Return a pointer to
+ * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the
+ * available space.
+ *
+ * Note: the tidaw-list is assumed to be contiguous with no ttics. The
+ * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize.
+ */
+struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count)
+{
+	if (itcw->num_tidaws >= itcw->max_tidaws)
+		return ERR_PTR(-ENOSPC);
+	return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count);
+}
+EXPORT_SYMBOL(itcw_add_tidaw);
+
+/**
+ * itcw_set_data - set data address and tida flag of the itcw
+ * @itcw: address of the itcw
+ * @addr: the data address
+ * @use_tidal: zero of the data address specifies a contiguous block of data,
+ * non-zero if it specifies a list if tidaws.
+ *
+ * Set the input/output data address of the itcw (depending on the value of the
+ * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
+ * is set as well.
+ */
+void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal)
+{
+	tcw_set_data(itcw->tcw, addr, use_tidal);
+}
+EXPORT_SYMBOL(itcw_set_data);
+
+/**
+ * itcw_finalize - calculate length and count fields of the itcw
+ * @itcw: address of the itcw
+ *
+ * Calculate tcw input-/output-count and tccbl fields and add a tcat the tccb.
+ * In case input- or output-tida is used, the tidaw-list must be stored in
+ * continuous storage (no ttic). The tcal field in the tccb must be
+ * up-to-date.
+ */
+void itcw_finalize(struct itcw *itcw)
+{
+	tcw_finalize(itcw->tcw, itcw->num_tidaws);
+}
+EXPORT_SYMBOL(itcw_finalize);
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
index 445cf36..2bf36e1 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -2082,7 +2082,6 @@
 	default:
 		BUG();
 	}
-	ccw_device_set_timeout(cdev, 0);
 	wake_up(&cdev->private->wait_q);
 }
 
@@ -2121,6 +2120,8 @@
 		case -EIO:
 			QDIO_PRINT_ERR("i/o error on device %s\n",
 				       cdev->dev.bus_id);
+			qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+			wake_up(&cdev->private->wait_q);
 			return;
 		case -ETIMEDOUT:
 			qdio_timeout_handler(cdev);
@@ -2139,8 +2140,8 @@
 	QDIO_DBF_TEXT4(0, trace, dbf_text);
 #endif /* CONFIG_QDIO_DEBUG */
 
-        cstat = irb->scsw.cstat;
-        dstat = irb->scsw.dstat;
+	cstat = irb->scsw.cmd.cstat;
+	dstat = irb->scsw.cmd.dstat;
 
 	switch (irq_ptr->state) {
 	case QDIO_IRQ_STATE_INACTIVE:
@@ -2353,9 +2354,6 @@
 {
 	char dbf_text[15];
 
-	if (!css_characteristics_avail)
-		return -EIO;
-
 	/* Check for bit 41. */
 	if (!css_general_characteristics.aif) {
 		QDIO_PRINT_WARN("Adapter interruption facility not " \
@@ -2667,12 +2665,12 @@
 		spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
 	} else if (rc == 0) {
 		qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP);
-		ccw_device_set_timeout(cdev, timeout);
 		spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags);
 
-		wait_event(cdev->private->wait_q,
-			   irq_ptr->state == QDIO_IRQ_STATE_INACTIVE ||
-			   irq_ptr->state == QDIO_IRQ_STATE_ERR);
+		wait_event_interruptible_timeout(cdev->private->wait_q,
+			irq_ptr->state == QDIO_IRQ_STATE_INACTIVE ||
+			irq_ptr->state == QDIO_IRQ_STATE_ERR,
+			timeout);
 	} else {
 		QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for "
 				"device %s\n", result, cdev->dev.bus_id);
@@ -2692,7 +2690,6 @@
 
 	/* Ignore errors. */
 	qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
-	ccw_device_set_timeout(cdev, 0);
 out:
 	up(&irq_ptr->setting_up_sema);
 	return result;
@@ -2907,13 +2904,10 @@
 	QDIO_DBF_TEXT0(0,setup,dbf_text);
 	QDIO_DBF_TEXT0(0,trace,dbf_text);
 
-	if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) {
-		ccw_device_set_timeout(cdev, 0);
+	if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat))
 		return;
-	}
 
 	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
-	ccw_device_set_timeout(cdev, 0);
 }
 
 int
@@ -3196,8 +3190,6 @@
 				irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
 				result, result2);
 		result=result2;
-		if (result)
-			ccw_device_set_timeout(cdev, 0);
 	}
 
 	spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags);
@@ -3279,7 +3271,6 @@
 
 	spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags);
 
-	ccw_device_set_timeout(cdev, 0);
 	ccw_device_set_options(cdev, CCWDEV_REPORT_ALL);
 	result=ccw_device_start(cdev,&irq_ptr->ccw,QDIO_DOING_ACTIVATE,
 				0, DOIO_DENY_PREFETCH);
@@ -3722,7 +3713,8 @@
 	char dbf_text[20];
 
 	tiqdio_ind =
-		s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL);
+		s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL,
+						TIQDIO_THININT_ISC);
 	if (IS_ERR(tiqdio_ind)) {
 		sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind));
 		QDIO_DBF_TEXT0(0,setup,dbf_text);
@@ -3738,7 +3730,8 @@
 tiqdio_unregister_thinints(void)
 {
 	if (tiqdio_ind)
-		s390_unregister_adapter_interrupt(tiqdio_ind);
+		s390_unregister_adapter_interrupt(tiqdio_ind,
+						  TIQDIO_THININT_ISC);
 }
 
 static int
@@ -3899,6 +3892,7 @@
 					    qdio_mempool_alloc,
 					    qdio_mempool_free, NULL);
 
+	isc_register(QDIO_AIRQ_ISC);
 	if (tiqdio_check_chsc_availability())
 		QDIO_PRINT_ERR("Not all CHSCs supported. Continuing.\n");
 
@@ -3911,6 +3905,7 @@
 cleanup_QDIO(void)
 {
 	tiqdio_unregister_thinints();
+	isc_unregister(QDIO_AIRQ_ISC);
 	qdio_remove_procfs_entry();
 	qdio_release_qdio_memory();
 	qdio_unregister_dbf_views();
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index c3df6b2..7656081 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -2,8 +2,8 @@
 #define _CIO_QDIO_H
 
 #include <asm/page.h>
-
-#include "schid.h"
+#include <asm/isc.h>
+#include <asm/schid.h>
 
 #ifdef CONFIG_QDIO_DEBUG
 #define QDIO_VERBOSE_LEVEL 9
@@ -26,7 +26,7 @@
  */
 #define IQDIO_FILL_LEVEL_TO_POLL 4
 
-#define TIQDIO_THININT_ISC 3
+#define TIQDIO_THININT_ISC QDIO_AIRQ_ISC
 #define TIQDIO_DELAY_TARGET 0
 #define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
 #define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
diff --git a/drivers/s390/cio/scsw.c b/drivers/s390/cio/scsw.c
new file mode 100644
index 0000000..f8da25a
--- /dev/null
+++ b/drivers/s390/cio/scsw.c
@@ -0,0 +1,843 @@
+/*
+ *  Helper functions for scsw access.
+ *
+ *    Copyright IBM Corp. 2008
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <asm/cio.h>
+#include "css.h"
+#include "chsc.h"
+
+/**
+ * scsw_is_tm - check for transport mode scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the specified scsw is a transport mode scsw, zero
+ * otherwise.
+ */
+int scsw_is_tm(union scsw *scsw)
+{
+	return css_general_characteristics.fcx && (scsw->tm.x == 1);
+}
+EXPORT_SYMBOL(scsw_is_tm);
+
+/**
+ * scsw_key - return scsw key field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the key field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_key(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.key;
+	else
+		return scsw->cmd.key;
+}
+EXPORT_SYMBOL(scsw_key);
+
+/**
+ * scsw_eswf - return scsw eswf field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the eswf field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_eswf(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.eswf;
+	else
+		return scsw->cmd.eswf;
+}
+EXPORT_SYMBOL(scsw_eswf);
+
+/**
+ * scsw_cc - return scsw cc field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the cc field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_cc(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.cc;
+	else
+		return scsw->cmd.cc;
+}
+EXPORT_SYMBOL(scsw_cc);
+
+/**
+ * scsw_ectl - return scsw ectl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the ectl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_ectl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.ectl;
+	else
+		return scsw->cmd.ectl;
+}
+EXPORT_SYMBOL(scsw_ectl);
+
+/**
+ * scsw_pno - return scsw pno field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the pno field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_pno(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.pno;
+	else
+		return scsw->cmd.pno;
+}
+EXPORT_SYMBOL(scsw_pno);
+
+/**
+ * scsw_fctl - return scsw fctl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the fctl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_fctl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.fctl;
+	else
+		return scsw->cmd.fctl;
+}
+EXPORT_SYMBOL(scsw_fctl);
+
+/**
+ * scsw_actl - return scsw actl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the actl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_actl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.actl;
+	else
+		return scsw->cmd.actl;
+}
+EXPORT_SYMBOL(scsw_actl);
+
+/**
+ * scsw_stctl - return scsw stctl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the stctl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_stctl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.stctl;
+	else
+		return scsw->cmd.stctl;
+}
+EXPORT_SYMBOL(scsw_stctl);
+
+/**
+ * scsw_dstat - return scsw dstat field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the dstat field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_dstat(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.dstat;
+	else
+		return scsw->cmd.dstat;
+}
+EXPORT_SYMBOL(scsw_dstat);
+
+/**
+ * scsw_cstat - return scsw cstat field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the cstat field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_cstat(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw->tm.cstat;
+	else
+		return scsw->cmd.cstat;
+}
+EXPORT_SYMBOL(scsw_cstat);
+
+/**
+ * scsw_cmd_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_key(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_key);
+
+/**
+ * scsw_cmd_is_valid_sctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_sctl(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_sctl);
+
+/**
+ * scsw_cmd_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_eswf(union scsw *scsw)
+{
+	return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_eswf);
+
+/**
+ * scsw_cmd_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_cc(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) &&
+	       (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_cc);
+
+/**
+ * scsw_cmd_is_valid_fmt - check fmt field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fmt field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_fmt(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_fmt);
+
+/**
+ * scsw_cmd_is_valid_pfch - check pfch field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pfch field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_pfch(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_pfch);
+
+/**
+ * scsw_cmd_is_valid_isic - check isic field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the isic field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_isic(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_isic);
+
+/**
+ * scsw_cmd_is_valid_alcc - check alcc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the alcc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_alcc(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_alcc);
+
+/**
+ * scsw_cmd_is_valid_ssi - check ssi field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ssi field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_ssi(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_ssi);
+
+/**
+ * scsw_cmd_is_valid_zcc - check zcc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the zcc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_zcc(union scsw *scsw)
+{
+	return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) &&
+	       (scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_zcc);
+
+/**
+ * scsw_cmd_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_ectl(union scsw *scsw)
+{
+	return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       !(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) &&
+	       (scsw->cmd.stctl & SCSW_STCTL_ALERT_STATUS);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_ectl);
+
+/**
+ * scsw_cmd_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_pno(union scsw *scsw)
+{
+	return (scsw->cmd.fctl != 0) &&
+	       (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       (!(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) ||
+		 ((scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) &&
+		  (scsw->cmd.actl & SCSW_ACTL_SUSPENDED)));
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_pno);
+
+/**
+ * scsw_cmd_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_fctl(union scsw *scsw)
+{
+	/* Only valid if pmcw.dnv == 1*/
+	return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_fctl);
+
+/**
+ * scsw_cmd_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_actl(union scsw *scsw)
+{
+	/* Only valid if pmcw.dnv == 1*/
+	return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_actl);
+
+/**
+ * scsw_cmd_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_stctl(union scsw *scsw)
+{
+	/* Only valid if pmcw.dnv == 1*/
+	return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_stctl);
+
+/**
+ * scsw_cmd_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_dstat(union scsw *scsw)
+{
+	return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       (scsw->cmd.cc != 3);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_dstat);
+
+/**
+ * scsw_cmd_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_cstat(union scsw *scsw)
+{
+	return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       (scsw->cmd.cc != 3);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_cstat);
+
+/**
+ * scsw_tm_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_key(union scsw *scsw)
+{
+	return (scsw->tm.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_key);
+
+/**
+ * scsw_tm_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_eswf(union scsw *scsw)
+{
+	return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_eswf);
+
+/**
+ * scsw_tm_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_cc(union scsw *scsw)
+{
+	return (scsw->tm.fctl & SCSW_FCTL_START_FUNC) &&
+	       (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_cc);
+
+/**
+ * scsw_tm_is_valid_fmt - check fmt field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fmt field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fmt(union scsw *scsw)
+{
+	return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fmt);
+
+/**
+ * scsw_tm_is_valid_x - check x field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the x field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_x(union scsw *scsw)
+{
+	return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_x);
+
+/**
+ * scsw_tm_is_valid_q - check q field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the q field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_q(union scsw *scsw)
+{
+	return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_q);
+
+/**
+ * scsw_tm_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_ectl(union scsw *scsw)
+{
+	return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       !(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) &&
+	       (scsw->tm.stctl & SCSW_STCTL_ALERT_STATUS);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_ectl);
+
+/**
+ * scsw_tm_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_pno(union scsw *scsw)
+{
+	return (scsw->tm.fctl != 0) &&
+	       (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       (!(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) ||
+		 ((scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) &&
+		  (scsw->tm.actl & SCSW_ACTL_SUSPENDED)));
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_pno);
+
+/**
+ * scsw_tm_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fctl(union scsw *scsw)
+{
+	/* Only valid if pmcw.dnv == 1*/
+	return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fctl);
+
+/**
+ * scsw_tm_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_actl(union scsw *scsw)
+{
+	/* Only valid if pmcw.dnv == 1*/
+	return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_actl);
+
+/**
+ * scsw_tm_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_stctl(union scsw *scsw)
+{
+	/* Only valid if pmcw.dnv == 1*/
+	return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_stctl);
+
+/**
+ * scsw_tm_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_dstat(union scsw *scsw)
+{
+	return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       (scsw->tm.cc != 3);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_dstat);
+
+/**
+ * scsw_tm_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_cstat(union scsw *scsw)
+{
+	return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+	       (scsw->tm.cc != 3);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_cstat);
+
+/**
+ * scsw_tm_is_valid_fcxs - check fcxs field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fcxs field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fcxs(union scsw *scsw)
+{
+	return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fcxs);
+
+/**
+ * scsw_tm_is_valid_schxs - check schxs field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the schxs field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_schxs(union scsw *scsw)
+{
+	return (scsw->tm.cstat & (SCHN_STAT_PROG_CHECK |
+				  SCHN_STAT_INTF_CTRL_CHK |
+				  SCHN_STAT_PROT_CHECK |
+				  SCHN_STAT_CHN_DATA_CHK));
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_schxs);
+
+/**
+ * scsw_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_actl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_actl(scsw);
+	else
+		return scsw_cmd_is_valid_actl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_actl);
+
+/**
+ * scsw_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_cc(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_cc(scsw);
+	else
+		return scsw_cmd_is_valid_cc(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_cc);
+
+/**
+ * scsw_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_cstat(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_cstat(scsw);
+	else
+		return scsw_cmd_is_valid_cstat(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_cstat);
+
+/**
+ * scsw_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_dstat(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_dstat(scsw);
+	else
+		return scsw_cmd_is_valid_dstat(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_dstat);
+
+/**
+ * scsw_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_ectl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_ectl(scsw);
+	else
+		return scsw_cmd_is_valid_ectl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_ectl);
+
+/**
+ * scsw_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_eswf(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_eswf(scsw);
+	else
+		return scsw_cmd_is_valid_eswf(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_eswf);
+
+/**
+ * scsw_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_fctl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_fctl(scsw);
+	else
+		return scsw_cmd_is_valid_fctl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_fctl);
+
+/**
+ * scsw_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_key(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_key(scsw);
+	else
+		return scsw_cmd_is_valid_key(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_key);
+
+/**
+ * scsw_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_pno(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_pno(scsw);
+	else
+		return scsw_cmd_is_valid_pno(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_pno);
+
+/**
+ * scsw_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_stctl(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_valid_stctl(scsw);
+	else
+		return scsw_cmd_is_valid_stctl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_stctl);
+
+/**
+ * scsw_cmd_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the command mode scsw indicates that the associated
+ * status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_cmd_is_solicited(union scsw *scsw)
+{
+	return (scsw->cmd.cc != 0) || (scsw->cmd.stctl !=
+		(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS));
+}
+EXPORT_SYMBOL(scsw_cmd_is_solicited);
+
+/**
+ * scsw_tm_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the transport mode scsw indicates that the associated
+ * status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_tm_is_solicited(union scsw *scsw)
+{
+	return (scsw->tm.cc != 0) || (scsw->tm.stctl !=
+		(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS));
+}
+EXPORT_SYMBOL(scsw_tm_is_solicited);
+
+/**
+ * scsw_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the transport or command mode scsw indicates that the
+ * associated status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_is_solicited(union scsw *scsw)
+{
+	if (scsw_is_tm(scsw))
+		return scsw_tm_is_solicited(scsw);
+	else
+		return scsw_cmd_is_solicited(scsw);
+}
+EXPORT_SYMBOL(scsw_is_solicited);
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index a1ab3e3..62b6b55 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -34,13 +34,15 @@
 #include <linux/mutex.h>
 #include <asm/s390_rdev.h>
 #include <asm/reset.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
 
 #include "ap_bus.h"
 
 /* Some prototypes. */
 static void ap_scan_bus(struct work_struct *);
 static void ap_poll_all(unsigned long);
-static void ap_poll_timeout(unsigned long);
+static enum hrtimer_restart ap_poll_timeout(struct hrtimer *);
 static int ap_poll_thread_start(void);
 static void ap_poll_thread_stop(void);
 static void ap_request_timeout(unsigned long);
@@ -80,12 +82,15 @@
 /*
  * Tasklet & timer for AP request polling.
  */
-static struct timer_list ap_poll_timer = TIMER_INITIALIZER(ap_poll_timeout,0,0);
 static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
 static atomic_t ap_poll_requests = ATOMIC_INIT(0);
 static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
 static struct task_struct *ap_poll_kthread = NULL;
 static DEFINE_MUTEX(ap_poll_thread_mutex);
+static struct hrtimer ap_poll_timer;
+/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
+ * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
+static unsigned long long poll_timeout = 250000;
 
 /**
  * ap_intructions_available() - Test if AP instructions are available.
@@ -636,11 +641,39 @@
 
 static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store);
 
+static ssize_t poll_timeout_show(struct bus_type *bus, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%llu\n", poll_timeout);
+}
+
+static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
+				  size_t count)
+{
+	unsigned long long time;
+	ktime_t hr_time;
+
+	/* 120 seconds = maximum poll interval */
+	if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000)
+		return -EINVAL;
+	poll_timeout = time;
+	hr_time = ktime_set(0, poll_timeout);
+
+	if (!hrtimer_is_queued(&ap_poll_timer) ||
+	    !hrtimer_forward(&ap_poll_timer, ap_poll_timer.expires, hr_time)) {
+		ap_poll_timer.expires = hr_time;
+		hrtimer_start(&ap_poll_timer, hr_time, HRTIMER_MODE_ABS);
+	}
+	return count;
+}
+
+static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store);
+
 static struct bus_attribute *const ap_bus_attrs[] = {
 	&bus_attr_ap_domain,
 	&bus_attr_config_time,
 	&bus_attr_poll_thread,
-	NULL
+	&bus_attr_poll_timeout,
+	NULL,
 };
 
 /**
@@ -895,9 +928,10 @@
  */
 static inline void ap_schedule_poll_timer(void)
 {
-	if (timer_pending(&ap_poll_timer))
+	if (hrtimer_is_queued(&ap_poll_timer))
 		return;
-	mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME);
+	hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout),
+		      HRTIMER_MODE_ABS);
 }
 
 /**
@@ -1115,13 +1149,14 @@
 
 /**
  * ap_poll_timeout(): AP receive polling for finished AP requests.
- * @unused: Unused variable.
+ * @unused: Unused pointer.
  *
- * Schedules the AP tasklet.
+ * Schedules the AP tasklet using a high resolution timer.
  */
-static void ap_poll_timeout(unsigned long unused)
+static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
 {
 	tasklet_schedule(&ap_tasklet);
+	return HRTIMER_NORESTART;
 }
 
 /**
@@ -1344,6 +1379,14 @@
 	ap_config_timer.expires = jiffies + ap_config_time * HZ;
 	add_timer(&ap_config_timer);
 
+	/* Setup the high resultion poll timer.
+	 * If we are running under z/VM adjust polling to z/VM polling rate.
+	 */
+	if (MACHINE_IS_VM)
+		poll_timeout = 1500000;
+	hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+	ap_poll_timer.function = ap_poll_timeout;
+
 	/* Start the low priority AP bus poll thread. */
 	if (ap_thread_flag) {
 		rc = ap_poll_thread_start();
@@ -1355,7 +1398,7 @@
 
 out_work:
 	del_timer_sync(&ap_config_timer);
-	del_timer_sync(&ap_poll_timer);
+	hrtimer_cancel(&ap_poll_timer);
 	destroy_workqueue(ap_work_queue);
 out_root:
 	s390_root_dev_unregister(ap_root_device);
@@ -1386,7 +1429,7 @@
 	ap_reset_domain();
 	ap_poll_thread_stop();
 	del_timer_sync(&ap_config_timer);
-	del_timer_sync(&ap_poll_timer);
+	hrtimer_cancel(&ap_poll_timer);
 	destroy_workqueue(ap_work_queue);
 	tasklet_kill(&ap_tasklet);
 	s390_root_dev_unregister(ap_root_device);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index c1e1200..446378b 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -92,6 +92,8 @@
 #define AP_DEVICE_TYPE_PCIXCC	5
 #define AP_DEVICE_TYPE_CEX2A	6
 #define AP_DEVICE_TYPE_CEX2C	7
+#define AP_DEVICE_TYPE_CEX2A2	8
+#define AP_DEVICE_TYPE_CEX2C2	9
 
 /*
  * AP reset flag states
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 4d36e80..8a4964f 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -1068,10 +1068,8 @@
 
 #define LBUFSIZE 1200UL
 	lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
-	if (!lbuf) {
-		PRINTK("kmalloc failed!\n");
+	if (!lbuf)
 		return 0;
-	}
 
 	local_count = min(LBUFSIZE - 1, count);
 	if (copy_from_user(lbuf, buffer, local_count) != 0) {
@@ -1081,23 +1079,15 @@
 	lbuf[local_count] = '\0';
 
 	ptr = strstr(lbuf, "Online devices");
-	if (!ptr) {
-		PRINTK("Unable to parse data (missing \"Online devices\")\n");
+	if (!ptr)
 		goto out;
-	}
 	ptr = strstr(ptr, "\n");
-	if (!ptr) {
-		PRINTK("Unable to parse data (missing newline "
-		       "after \"Online devices\")\n");
+	if (!ptr)
 		goto out;
-	}
 	ptr++;
 
-	if (strstr(ptr, "Waiting work element counts") == NULL) {
-		PRINTK("Unable to parse data (missing "
-		       "\"Waiting work element counts\")\n");
+	if (strstr(ptr, "Waiting work element counts") == NULL)
 		goto out;
-	}
 
 	for (j = 0; j < 64 && *ptr; ptr++) {
 		/*
@@ -1197,16 +1187,12 @@
 
 	/* Register the request sprayer. */
 	rc = misc_register(&zcrypt_misc_device);
-	if (rc < 0) {
-		PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n",
-			zcrypt_misc_device.minor, rc);
+	if (rc < 0)
 		goto out;
-	}
 
 	/* Set up the proc file system */
 	zcrypt_entry = create_proc_entry("driver/z90crypt", 0644, NULL);
 	if (!zcrypt_entry) {
-		PRINTK("Couldn't create z90crypt proc entry\n");
 		rc = -ENOMEM;
 		goto out_misc;
 	}
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 5c6e222..1d1ec74 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -30,34 +30,6 @@
 #ifndef _ZCRYPT_API_H_
 #define _ZCRYPT_API_H_
 
-/**
- * Macro definitions
- *
- * PDEBUG debugs in the form "zcrypt: function_name -> message"
- *
- * PRINTK is like PDEBUG, except that it is always enabled
- * PRINTKN is like PRINTK, except that it does not include the function name
- * PRINTKW is like PRINTK, except that it uses KERN_WARNING
- * PRINTKC is like PRINTK, except that it uses KERN_CRIT
- */
-#define DEV_NAME	"zcrypt"
-
-#define PRINTK(fmt, args...) \
-	printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __func__ , ## args)
-#define PRINTKN(fmt, args...) \
-	printk(KERN_DEBUG DEV_NAME ": " fmt, ## args)
-#define PRINTKW(fmt, args...) \
-	printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __func__ , ## args)
-#define PRINTKC(fmt, args...) \
-	printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __func__ , ## args)
-
-#ifdef ZCRYPT_DEBUG
-#define PDEBUG(fmt, args...) \
-	printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __func__ , ## args)
-#else
-#define PDEBUG(fmt, args...) do {} while (0)
-#endif
-
 #include "ap_bus.h"
 #include <asm/zcrypt.h>
 
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 08657f6..54f4cbc 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -49,6 +49,7 @@
 
 static struct ap_device_id zcrypt_cex2a_ids[] = {
 	{ AP_DEVICE(AP_DEVICE_TYPE_CEX2A) },
+	{ AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) },
 	{ /* end of list */ },
 };
 
@@ -242,9 +243,6 @@
 		return convert_type80(zdev, reply,
 				      outputdata, outputdatalength);
 	default: /* Unknown response type, this should NEVER EVER happen */
-		PRINTK("Unrecognized Message Header: %08x%08x\n",
-		       *(unsigned int *) reply->message,
-		       *(unsigned int *) (reply->message+4));
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index 3e27fe7..03ba27f 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -92,10 +92,6 @@
 {
 	struct error_hdr *ehdr = reply->message;
 
-	PRINTK("Hardware error : Type %02x Message Header: %08x%08x\n",
-	       ehdr->type, *(unsigned int *) reply->message,
-	       *(unsigned int *) (reply->message + 4));
-
 	switch (ehdr->reply_code) {
 	case REP82_ERROR_OPERAND_INVALID:
 	case REP82_ERROR_OPERAND_SIZE:
@@ -123,8 +119,6 @@
 		zdev->online = 0;
 		return -EAGAIN;
 	default:
-		PRINTKW("unknown type %02x reply code = %d\n",
-			ehdr->type, ehdr->reply_code);
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index 6e93b47..12da481 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -226,9 +226,6 @@
 		return convert_type84(zdev, reply,
 				      outputdata, outputdatalength);
 	default: /* Unknown response type, this should NEVER EVER happen */
-		PRINTK("Unrecognized Message Header: %08x%08x\n",
-		       *(unsigned int *) reply->message,
-		       *(unsigned int *) (reply->message+4));
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
index 17ea56c..779952c 100644
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -361,26 +361,18 @@
 	service_rc = le16_to_cpu(msg->cprb.ccp_rtcode);
 	if (unlikely(service_rc != 0)) {
 		service_rs = le16_to_cpu(msg->cprb.ccp_rscode);
-		if (service_rc == 8 && service_rs == 66) {
-			PDEBUG("Bad block format on PCICC\n");
+		if (service_rc == 8 && service_rs == 66)
 			return -EINVAL;
-		}
-		if (service_rc == 8 && service_rs == 65) {
-			PDEBUG("Probably an even modulus on PCICC\n");
+		if (service_rc == 8 && service_rs == 65)
 			return -EINVAL;
-		}
 		if (service_rc == 8 && service_rs == 770) {
-			PDEBUG("Invalid key length on PCICC\n");
 			zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD;
 			return -EAGAIN;
 		}
 		if (service_rc == 8 && service_rs == 783) {
-			PDEBUG("Extended bitlengths not enabled on PCICC\n");
 			zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD;
 			return -EAGAIN;
 		}
-		PRINTK("Unknown service rc/rs (PCICC): %d/%d\n",
-		       service_rc, service_rs);
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
@@ -434,9 +426,6 @@
 					      outputdata, outputdatalength);
 		/* no break, incorrect cprb version is an unknown response */
 	default: /* Unknown response type, this should NEVER EVER happen */
-		PRINTK("Unrecognized Message Header: %08x%08x\n",
-		       *(unsigned int *) reply->message,
-		       *(unsigned int *) (reply->message+4));
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index 0bc9b31..d8ad36f 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -72,6 +72,7 @@
 static struct ap_device_id zcrypt_pcixcc_ids[] = {
 	{ AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) },
 	{ AP_DEVICE(AP_DEVICE_TYPE_CEX2C) },
+	{ AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) },
 	{ /* end of list */ },
 };
 
@@ -289,38 +290,19 @@
 	ap_msg->length = sizeof(struct type6_hdr) +
 		CEIL4(xcRB->request_control_blk_length) +
 		xcRB->request_data_length;
-	if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE) {
-		PRINTK("Combined message is too large (%ld/%d/%d).\n",
-		    sizeof(struct type6_hdr),
-		    xcRB->request_control_blk_length,
-		    xcRB->request_data_length);
+	if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
 		return -EFAULT;
-	}
-	if (CEIL4(xcRB->reply_control_blk_length) >
-	    PCIXCC_MAX_XCRB_REPLY_SIZE) {
-		PDEBUG("Reply CPRB length is too large (%d).\n",
-		    xcRB->request_control_blk_length);
+	if (CEIL4(xcRB->reply_control_blk_length) > PCIXCC_MAX_XCRB_REPLY_SIZE)
 		return -EFAULT;
-	}
-	if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE) {
-		PDEBUG("Reply data block length is too large (%d).\n",
-		    xcRB->reply_data_length);
+	if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE)
 		return -EFAULT;
-	}
 	replylen = CEIL4(xcRB->reply_control_blk_length) +
 		CEIL4(xcRB->reply_data_length) +
 		sizeof(struct type86_fmt2_msg);
 	if (replylen > PCIXCC_MAX_XCRB_RESPONSE_SIZE) {
-		PDEBUG("Reply CPRB + data block > PCIXCC_MAX_XCRB_RESPONSE_SIZE"
-		       " (%d/%d/%d).\n",
-		       sizeof(struct type86_fmt2_msg),
-		       xcRB->reply_control_blk_length,
-		       xcRB->reply_data_length);
 		xcRB->reply_control_blk_length = PCIXCC_MAX_XCRB_RESPONSE_SIZE -
 			(sizeof(struct type86_fmt2_msg) +
 			    CEIL4(xcRB->reply_data_length));
-		PDEBUG("Capping Reply CPRB length at %d\n",
-		       xcRB->reply_control_blk_length);
 	}
 
 	/* prepare type6 header */
@@ -339,11 +321,8 @@
 		    xcRB->request_control_blk_length))
 		return -EFAULT;
 	if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
-	    xcRB->request_control_blk_length) {
-		PDEBUG("cprb_len too large (%d/%d)\n", msg->cprbx.cprb_len,
-		    xcRB->request_control_blk_length);
+	    xcRB->request_control_blk_length)
 		return -EFAULT;
-	}
 	function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
 	memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
 
@@ -471,29 +450,18 @@
 	service_rc = msg->cprbx.ccp_rtcode;
 	if (unlikely(service_rc != 0)) {
 		service_rs = msg->cprbx.ccp_rscode;
-		if (service_rc == 8 && service_rs == 66) {
-			PDEBUG("Bad block format on PCIXCC/CEX2C\n");
+		if (service_rc == 8 && service_rs == 66)
 			return -EINVAL;
-		}
-		if (service_rc == 8 && service_rs == 65) {
-			PDEBUG("Probably an even modulus on PCIXCC/CEX2C\n");
+		if (service_rc == 8 && service_rs == 65)
 			return -EINVAL;
-		}
-		if (service_rc == 8 && service_rs == 770) {
-			PDEBUG("Invalid key length on PCIXCC/CEX2C\n");
+		if (service_rc == 8 && service_rs == 770)
 			return -EINVAL;
-		}
 		if (service_rc == 8 && service_rs == 783) {
-			PDEBUG("Extended bitlengths not enabled on PCIXCC/CEX2C\n");
 			zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
 			return -EAGAIN;
 		}
-		if (service_rc == 12 && service_rs == 769) {
-			PDEBUG("Invalid key on PCIXCC/CEX2C\n");
+		if (service_rc == 12 && service_rs == 769)
 			return -EINVAL;
-		}
-		PRINTK("Unknown service rc/rs (PCIXCC/CEX2C): %d/%d\n",
-		       service_rc, service_rs);
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
@@ -569,11 +537,8 @@
 	} __attribute__((packed)) *msg = reply->message;
 	char *data = reply->message;
 
-	if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0) {
-		PDEBUG("RNG response error on PCIXCC/CEX2C rc=%hu/rs=%hu\n",
-		       rc, rs);
+	if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
 		return -EINVAL;
-	}
 	memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
 	return msg->fmt2.count2;
 }
@@ -598,9 +563,6 @@
 						  outputdata, outputdatalength);
 		/* no break, incorrect cprb version is an unknown response */
 	default: /* Unknown response type, this should NEVER EVER happen */
-		PRINTK("Unrecognized Message Header: %08x%08x\n",
-		       *(unsigned int *) reply->message,
-		       *(unsigned int *) (reply->message+4));
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
@@ -627,9 +589,6 @@
 			return convert_type86_xcrb(zdev, reply, xcRB);
 		/* no break, incorrect cprb version is an unknown response */
 	default: /* Unknown response type, this should NEVER EVER happen */
-		PRINTK("Unrecognized Message Header: %08x%08x\n",
-		       *(unsigned int *) reply->message,
-		       *(unsigned int *) (reply->message+4));
 		xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
@@ -653,9 +612,6 @@
 			return convert_type86_rng(zdev, reply, data);
 		/* no break, incorrect cprb version is an unknown response */
 	default: /* Unknown response type, this should NEVER EVER happen */
-		PRINTK("Unrecognized Message Header: %08x%08x\n",
-		       *(unsigned int *) reply->message,
-		       *(unsigned int *) (reply->message+4));
 		zdev->online = 0;
 		return -EAGAIN;	/* repeat the request on a different device. */
 	}
@@ -700,10 +656,7 @@
 			memcpy(msg->message, reply->message, length);
 			break;
 		default:
-			PRINTK("Invalid internal response type: %i\n",
-			    resp_type->type);
-			memcpy(msg->message, &error_reply,
-			    sizeof error_reply);
+			memcpy(msg->message, &error_reply, sizeof error_reply);
 		}
 	} else
 		memcpy(msg->message, reply->message, sizeof error_reply);
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 04a1d7b..c644669 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -703,7 +703,8 @@
 	if (!cdev->dev.driver_data) {
                 printk(KERN_WARNING "claw: unsolicited interrupt for device:"
 		 	"%s received c-%02x d-%02x\n",
-                        cdev->dev.bus_id,irb->scsw.cstat, irb->scsw.dstat);
+		       cdev->dev.bus_id, irb->scsw.cmd.cstat,
+		       irb->scsw.cmd.dstat);
 #ifdef FUNCTRACE
                 printk(KERN_INFO "claw: %s() "
 			"exit on line %d\n",__func__,__LINE__);
@@ -732,22 +733,23 @@
 #ifdef IOTRACE
         printk(KERN_INFO "%s: interrupt for device: %04x "
 		"received c-%02x d-%02x state-%02x\n",
-                dev->name, p_ch->devno, irb->scsw.cstat,
-		irb->scsw.dstat, p_ch->claw_state);
+	       dev->name, p_ch->devno, irb->scsw.cmd.cstat,
+	       irb->scsw.cmd.dstat, p_ch->claw_state);
 #endif
 
 	/* Copy interruption response block. */
 	memcpy(p_ch->irb, irb, sizeof(struct irb));
 
         /* Check for good subchannel return code, otherwise error message */
-        if (irb->scsw.cstat  &&  !(irb->scsw.cstat & SCHN_STAT_PCI)) {
+	if (irb->scsw.cmd.cstat && !(irb->scsw.cmd.cstat & SCHN_STAT_PCI)) {
                 printk(KERN_INFO "%s: subchannel check for device: %04x -"
 			" Sch Stat %02x  Dev Stat %02x CPA - %04x\n",
                         dev->name, p_ch->devno,
-			irb->scsw.cstat, irb->scsw.dstat,irb->scsw.cpa);
+			irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+			irb->scsw.cmd.cpa);
 #ifdef IOTRACE
 		dumpit((char *)irb,sizeof(struct irb));
-		dumpit((char *)(unsigned long)irb->scsw.cpa,
+		dumpit((char *)(unsigned long)irb->scsw.cmd.cpa,
 			sizeof(struct ccw1));
 #endif
 #ifdef FUNCTRACE
@@ -759,22 +761,24 @@
         }
 
         /* Check the reason-code of a unit check */
-        if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
                 ccw_check_unit_check(p_ch, irb->ecw[0]);
-        }
 
         /* State machine to bring the connection up, down and to restart */
-        p_ch->last_dstat = irb->scsw.dstat;
+	p_ch->last_dstat = irb->scsw.cmd.dstat;
 
         switch (p_ch->claw_state) {
                 case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */
 #ifdef DEBUGMSG
                         printk(KERN_INFO "%s: CLAW_STOP enter\n", dev->name);
 #endif
-                        if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
-	    		(p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
-	    		(p_ch->irb->scsw.stctl ==
-	     		(SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+			if (!((p_ch->irb->scsw.cmd.stctl &
+			       SCSW_STCTL_SEC_STATUS) ||
+			    (p_ch->irb->scsw.cmd.stctl ==
+				SCSW_STCTL_STATUS_PEND) ||
+			    (p_ch->irb->scsw.cmd.stctl ==
+				(SCSW_STCTL_ALERT_STATUS |
+				 SCSW_STCTL_STATUS_PEND)))) {
 #ifdef FUNCTRACE
                                 printk(KERN_INFO "%s:%s Exit on line %d\n",
 					dev->name,__func__,__LINE__);
@@ -798,10 +802,13 @@
                         printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO\n",
 				dev->name);
 #endif
-                        if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
-	    		(p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
-	    		(p_ch->irb->scsw.stctl ==
-	     		(SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+			if (!((p_ch->irb->scsw.cmd.stctl &
+			       SCSW_STCTL_SEC_STATUS) ||
+			    (p_ch->irb->scsw.cmd.stctl ==
+			     SCSW_STCTL_STATUS_PEND) ||
+			    (p_ch->irb->scsw.cmd.stctl ==
+			     (SCSW_STCTL_ALERT_STATUS |
+			      SCSW_STCTL_STATUS_PEND)))) {
 #ifdef FUNCTRACE
 				printk(KERN_INFO "%s:%s Exit on line %d\n",
 					dev->name,__func__,__LINE__);
@@ -828,8 +835,8 @@
 					"interrupt for device:"
 				 	"%s received c-%02x d-%02x\n",
                 		        cdev->dev.bus_id,
-					irb->scsw.cstat,
-					irb->scsw.dstat);
+					irb->scsw.cmd.cstat,
+					irb->scsw.cmd.dstat);
 				return;
 				}
 #ifdef DEBUGMSG
@@ -844,7 +851,7 @@
                         return;
                 case CLAW_START_READ:
 			CLAW_DBF_TEXT(4,trace,"ReadIRQ");
-                        if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+			if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
                                 clear_bit(0, (void *)&p_ch->IO_active);
                                 if ((p_ch->irb->ecw[0] & 0x41) == 0x41 ||
                                     (p_ch->irb->ecw[0] & 0x40) == 0x40 ||
@@ -863,8 +870,8 @@
 					CLAW_DBF_TEXT(4,trace,"notrdy");
                                         return;
                         }
-                        if ((p_ch->irb->scsw.cstat & SCHN_STAT_PCI) &&
-			    (p_ch->irb->scsw.dstat==0)) {
+			if ((p_ch->irb->scsw.cmd.cstat & SCHN_STAT_PCI) &&
+			    (p_ch->irb->scsw.cmd.dstat == 0)) {
                                 if (test_and_set_bit(CLAW_BH_ACTIVE,
 					(void *)&p_ch->flag_a) == 0) {
 					tasklet_schedule(&p_ch->tasklet);
@@ -879,10 +886,13 @@
 				CLAW_DBF_TEXT(4,trace,"PCI_read");
                                 return;
                         }
-                        if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
-	    		 (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
-	    		 (p_ch->irb->scsw.stctl ==
-	     		 (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+			if (!((p_ch->irb->scsw.cmd.stctl &
+			       SCSW_STCTL_SEC_STATUS) ||
+			     (p_ch->irb->scsw.cmd.stctl ==
+			      SCSW_STCTL_STATUS_PEND) ||
+			     (p_ch->irb->scsw.cmd.stctl ==
+			      (SCSW_STCTL_ALERT_STATUS |
+			       SCSW_STCTL_STATUS_PEND)))) {
 #ifdef FUNCTRACE
 				printk(KERN_INFO "%s:%s Exit on line %d\n",
 					dev->name,__func__,__LINE__);
@@ -911,7 +921,7 @@
 			CLAW_DBF_TEXT(4,trace,"RdIRQXit");
                         return;
                 case CLAW_START_WRITE:
-                        if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+			if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
                                 printk(KERN_INFO "%s: Unit Check Occured in "
 					"write channel\n",dev->name);
                                 clear_bit(0, (void *)&p_ch->IO_active);
@@ -934,16 +944,19 @@
 				CLAW_DBF_TEXT(4,trace,"rstrtwrt");
                                 return;
                         }
-                        if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
+			if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
                                         clear_bit(0, (void *)&p_ch->IO_active);
                                         printk(KERN_INFO "%s: Unit Exception "
 						"Occured in write channel\n",
 						dev->name);
                         }
-                        if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
-	    		(p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
-	    		(p_ch->irb->scsw.stctl ==
-	     		(SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+			if (!((p_ch->irb->scsw.cmd.stctl &
+			       SCSW_STCTL_SEC_STATUS) ||
+			     (p_ch->irb->scsw.cmd.stctl ==
+			      SCSW_STCTL_STATUS_PEND) ||
+			     (p_ch->irb->scsw.cmd.stctl ==
+			      (SCSW_STCTL_ALERT_STATUS |
+			       SCSW_STCTL_STATUS_PEND)))) {
 #ifdef FUNCTRACE
 				printk(KERN_INFO "%s:%s Exit on line %d\n",
 					dev->name,__func__,__LINE__);
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 2a106f3..7e6bd38 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -257,9 +257,9 @@
 	if (duration > ch->prof.tx_time)
 		ch->prof.tx_time = duration;
 
-	if (ch->irb->scsw.count != 0)
+	if (ch->irb->scsw.cmd.count != 0)
 		ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
-			     dev->name, ch->irb->scsw.count);
+			     dev->name, ch->irb->scsw.cmd.count);
 	fsm_deltimer(&ch->timer);
 	while ((skb = skb_dequeue(&ch->io_queue))) {
 		priv->stats.tx_packets++;
@@ -353,7 +353,7 @@
 	struct channel *ch = arg;
 	struct net_device *dev = ch->netdev;
 	struct ctcm_priv *priv = dev->priv;
-	int len = ch->max_bufsize - ch->irb->scsw.count;
+	int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
 	struct sk_buff *skb = ch->trans_skb;
 	__u16 block_len = *((__u16 *)skb->data);
 	int check_len;
@@ -1234,9 +1234,9 @@
 	if (duration > ch->prof.tx_time)
 		ch->prof.tx_time = duration;
 
-	if (ch->irb->scsw.count != 0)
+	if (ch->irb->scsw.cmd.count != 0)
 		ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
-				dev->name, ch->irb->scsw.count);
+				dev->name, ch->irb->scsw.cmd.count);
 	fsm_deltimer(&ch->timer);
 	while ((skb = skb_dequeue(&ch->io_queue))) {
 		priv->stats.tx_packets++;
@@ -1394,7 +1394,7 @@
 	struct sk_buff		*skb = ch->trans_skb;
 	struct sk_buff		*new_skb;
 	unsigned long	saveflags = 0;	/* avoids compiler warning */
-	int len	= ch->max_bufsize - ch->irb->scsw.count;
+	int len	= ch->max_bufsize - ch->irb->scsw.cmd.count;
 
 	if (do_debug_data) {
 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx %s cp:%i %s\n",
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index d52843d..6b13c1c 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -1236,8 +1236,8 @@
 	/* Check for unsolicited interrupts. */
 	if (cgdev == NULL) {
 		ctcm_pr_warn("ctcm: Got unsolicited irq: %s c-%02x d-%02x\n",
-			    cdev->dev.bus_id, irb->scsw.cstat,
-			    irb->scsw.dstat);
+			    cdev->dev.bus_id, irb->scsw.cmd.cstat,
+			    irb->scsw.cmd.dstat);
 		return;
 	}
 
@@ -1266,40 +1266,40 @@
 				"received c-%02x d-%02x\n",
 				dev->name,
 				ch->id,
-				irb->scsw.cstat,
-				irb->scsw.dstat);
+				irb->scsw.cmd.cstat,
+				irb->scsw.cmd.dstat);
 
 	/* Copy interruption response block. */
 	memcpy(ch->irb, irb, sizeof(struct irb));
 
 	/* Check for good subchannel return code, otherwise error message */
-	if (irb->scsw.cstat) {
+	if (irb->scsw.cmd.cstat) {
 		fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
 		ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n",
-			    dev->name, ch->id, irb->scsw.cstat,
-			    irb->scsw.dstat);
+			    dev->name, ch->id, irb->scsw.cmd.cstat,
+			    irb->scsw.cmd.dstat);
 		return;
 	}
 
 	/* Check the reason-code of a unit check */
-	if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
 		ccw_unit_check(ch, irb->ecw[0]);
 		return;
 	}
-	if (irb->scsw.dstat & DEV_STAT_BUSY) {
-		if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+	if (irb->scsw.cmd.dstat & DEV_STAT_BUSY) {
+		if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
 			fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch);
 		else
 			fsm_event(ch->fsm, CTC_EVENT_BUSY, ch);
 		return;
 	}
-	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
 		fsm_event(ch->fsm, CTC_EVENT_ATTN, ch);
 		return;
 	}
-	if ((irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
-	    (irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
-	    (irb->scsw.stctl ==
+	if ((irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+	    (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+	    (irb->scsw.cmd.stctl ==
 	     (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
 		fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch);
 	else
diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c
index 8e76973..f4a3237 100644
--- a/drivers/s390/net/cu3088.c
+++ b/drivers/s390/net/cu3088.c
@@ -36,7 +36,6 @@
 	"CTC/A",
 	"ESCON channel",
 	"FICON channel",
-	"P390 LCS card",
 	"OSA LCS card",
 	"CLAW channel device",
 	"unknown channel type",
@@ -49,7 +48,6 @@
 	{ CCW_DEVICE(0x3088, 0x08), .driver_info = channel_type_parallel },
 	{ CCW_DEVICE(0x3088, 0x1f), .driver_info = channel_type_escon },
 	{ CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon },
-	{ CCW_DEVICE(0x3088, 0x01), .driver_info = channel_type_p390 },
 	{ CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 },
 	{ CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw },
 	{ /* end of list */ }
diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h
index 1753661..d8558a7 100644
--- a/drivers/s390/net/cu3088.h
+++ b/drivers/s390/net/cu3088.h
@@ -17,9 +17,6 @@
 	/* Device is a FICON channel */
 	channel_type_ficon,
 
-	/* Device is a P390 LCS card */
-	channel_type_p390,
-
 	/* Device is a OSA2 card */
 	channel_type_osa2,
 
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index dd22f4b..6de2838 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1327,8 +1327,8 @@
 	char *sense;
 
 	sense = (char *) irb->ecw;
-	cstat = irb->scsw.cstat;
-	dstat = irb->scsw.dstat;
+	cstat = irb->scsw.cmd.cstat;
+	dstat = irb->scsw.cmd.dstat;
 
 	if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
 		     SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
@@ -1388,11 +1388,13 @@
 	else
 		channel = &card->write;
 
-	cstat = irb->scsw.cstat;
-	dstat = irb->scsw.dstat;
+	cstat = irb->scsw.cmd.cstat;
+	dstat = irb->scsw.cmd.dstat;
 	LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id);
-	LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.cstat, irb->scsw.dstat);
-	LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.fctl, irb->scsw.actl);
+	LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.cstat,
+		      irb->scsw.cmd.dstat);
+	LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.fctl,
+		      irb->scsw.cmd.actl);
 
 	/* Check for channel and device errors presented */
 	rc = lcs_get_problem(cdev, irb);
@@ -1410,11 +1412,11 @@
 	}
 	/* How far in the ccw chain have we processed? */
 	if ((channel->state != LCS_CH_STATE_INIT) &&
-	    (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) {
-		index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa)
+	    (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC)) {
+		index = (struct ccw1 *) __va((addr_t) irb->scsw.cmd.cpa)
 			- channel->ccws;
-		if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) ||
-		    (irb->scsw.cstat & SCHN_STAT_PCI))
+		if ((irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED) ||
+		    (irb->scsw.cmd.cstat & SCHN_STAT_PCI))
 			/* Bloody io subsystem tells us lies about cpa... */
 			index = (index - 1) & (LCS_NUM_BUFFS - 1);
 		while (channel->io_idx != index) {
@@ -1425,25 +1427,24 @@
 		}
 	}
 
-	if ((irb->scsw.dstat & DEV_STAT_DEV_END) ||
-	    (irb->scsw.dstat & DEV_STAT_CHN_END) ||
-	    (irb->scsw.dstat & DEV_STAT_UNIT_CHECK))
+	if ((irb->scsw.cmd.dstat & DEV_STAT_DEV_END) ||
+	    (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) ||
+	    (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK))
 		/* Mark channel as stopped. */
 		channel->state = LCS_CH_STATE_STOPPED;
-	else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED)
+	else if (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED)
 		/* CCW execution stopped on a suspend bit. */
 		channel->state = LCS_CH_STATE_SUSPENDED;
-	if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
-		if (irb->scsw.cc != 0) {
+	if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
+		if (irb->scsw.cmd.cc != 0) {
 			ccw_device_halt(channel->ccwdev, (addr_t) channel);
 			return;
 		}
 		/* The channel has been stopped by halt_IO. */
 		channel->state = LCS_CH_STATE_HALTED;
 	}
-	if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
+	if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
 		channel->state = LCS_CH_STATE_CLEARED;
-	}
 	/* Do the rest in the tasklet. */
 	tasklet_schedule(&channel->irq_tasklet);
 }
@@ -1761,7 +1762,7 @@
 				netif_carrier_off(card->dev);
 			break;
 		default:
-			PRINT_INFO("UNRECOGNIZED LGW COMMAND\n");
+			LCS_DBF_TEXT(5, trace, "noLGWcmd");
 			break;
 		}
 	} else
@@ -2042,13 +2043,12 @@
 	LCS_DBF_TEXT(2, setup, "add_dev");
         card = lcs_alloc_card();
         if (!card) {
-                PRINT_ERR("Allocation of lcs card failed\n");
+		LCS_DBF_TEXT_(2, setup, "  rc%d", -ENOMEM);
 		put_device(&ccwgdev->dev);
                 return -ENOMEM;
         }
 	ret = sysfs_create_group(&ccwgdev->dev.kobj, &lcs_attr_group);
 	if (ret) {
-                PRINT_ERR("Creating attributes failed");
 		lcs_free_card(card);
 		put_device(&ccwgdev->dev);
 		return ret;
@@ -2140,7 +2140,6 @@
 	default:
 		LCS_DBF_TEXT(3, setup, "errinit");
 		PRINT_ERR("LCS: Initialization failed\n");
-		PRINT_ERR("LCS: No device found!\n");
 		goto out;
 	}
 	if (!dev)
@@ -2269,7 +2268,6 @@
 	if (!card)
 		return;
 
-	PRINT_INFO("Removing lcs group device ....\n");
 	LCS_DBF_TEXT(3, setup, "remdev");
 	LCS_DBF_HEX(3, setup, &card, sizeof(void*));
 	if (ccwgdev->state == CCWGROUP_ONLINE) {
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index e4ba6a0..9242b5a 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -625,9 +625,6 @@
 		offset += header->next;
 		header->next -= NETIUCV_HDRLEN;
 		if (skb_tailroom(pskb) < header->next) {
-			PRINT_WARN("%s: Illegal next field in iucv header: "
-			       "%d > %d\n",
-			       dev->name, header->next, skb_tailroom(pskb));
 			IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n",
 				header->next, skb_tailroom(pskb));
 			return;
@@ -636,8 +633,6 @@
 		skb_reset_mac_header(pskb);
 		skb = dev_alloc_skb(pskb->len);
 		if (!skb) {
-			PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n",
-			       dev->name);
 			IUCV_DBF_TEXT(data, 2,
 				"Out of memory in netiucv_unpack_skb\n");
 			privptr->stats.rx_dropped++;
@@ -674,7 +669,6 @@
 
 	if (!conn->netdev) {
 		iucv_message_reject(conn->path, msg);
-		PRINT_WARN("Received data for unlinked connection\n");
 		IUCV_DBF_TEXT(data, 2,
 			      "Received data for unlinked connection\n");
 		return;
@@ -682,8 +676,6 @@
 	if (msg->length > conn->max_buffsize) {
 		iucv_message_reject(conn->path, msg);
 		privptr->stats.rx_dropped++;
-		PRINT_WARN("msglen %d > max_buffsize %d\n",
-			   msg->length, conn->max_buffsize);
 		IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n",
 			       msg->length, conn->max_buffsize);
 		return;
@@ -695,7 +687,6 @@
 				  msg->length, NULL);
 	if (rc || msg->length < 5) {
 		privptr->stats.rx_errors++;
-		PRINT_WARN("iucv_receive returned %08x\n", rc);
 		IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc);
 		return;
 	}
@@ -778,7 +769,6 @@
 		fsm_newstate(fi, CONN_STATE_IDLE);
 		if (privptr)
 			privptr->stats.tx_errors += txpackets;
-		PRINT_WARN("iucv_send returned %08x\n",	rc);
 		IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
 	} else {
 		if (privptr) {
@@ -806,8 +796,6 @@
 	path->flags = 0;
 	rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
 	if (rc) {
-		PRINT_WARN("%s: IUCV accept failed with error %d\n",
-		       netdev->name, rc);
 		IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
 		return;
 	}
@@ -873,7 +861,7 @@
 	IUCV_DBF_TEXT(trace, 3, __func__);
 
 	fsm_newstate(fi, CONN_STATE_STARTWAIT);
-	PRINT_DEBUG("%s('%s'): connecting ...\n",
+	IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
 		    conn->netdev->name, conn->userid);
 
 	/*
@@ -968,8 +956,8 @@
 	struct iucv_connection *conn = arg;
 	struct net_device *netdev = conn->netdev;
 
-	PRINT_WARN("%s: Cannot connect without username\n", netdev->name);
-	IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n");
+	IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n",
+		netdev->name, conn->userid);
 }
 
 static const fsm_node conn_fsm[] = {
@@ -1077,9 +1065,6 @@
 				"connection is up and running\n");
 			break;
 		case DEV_STATE_STOPWAIT:
-			PRINT_INFO(
-			       "%s: got connection UP event during shutdown!\n",
-			       dev->name);
 			IUCV_DBF_TEXT(data, 2,
 				"dev_action_connup: in DEV_STATE_STOPWAIT\n");
 			break;
@@ -1174,8 +1159,6 @@
 			nskb = alloc_skb(skb->len + NETIUCV_HDRLEN +
 					 NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA);
 			if (!nskb) {
-				PRINT_WARN("%s: Could not allocate tx_skb\n",
-				       conn->netdev->name);
 				IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n");
 				rc = -ENOMEM;
 				return rc;
@@ -1223,7 +1206,6 @@
 				skb_pull(skb, NETIUCV_HDRLEN);
 				skb_trim(skb, skb->len - NETIUCV_HDRLEN);
 			}
-			PRINT_WARN("iucv_send returned %08x\n",	rc);
 			IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc);
 		} else {
 			if (copied)
@@ -1293,14 +1275,11 @@
 	 * Some sanity checks ...
 	 */
 	if (skb == NULL) {
-		PRINT_WARN("%s: NULL sk_buff passed\n", dev->name);
 		IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n");
 		privptr->stats.tx_dropped++;
 		return 0;
 	}
 	if (skb_headroom(skb) < NETIUCV_HDRLEN) {
-		PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n",
-		       dev->name, NETIUCV_HDRLEN);
 		IUCV_DBF_TEXT(data, 2,
 			"netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n");
 		dev_kfree_skb(skb);
@@ -1393,7 +1372,6 @@
 
 	IUCV_DBF_TEXT(trace, 3, __func__);
 	if (count > 9) {
-		PRINT_WARN("netiucv: username too long (%d)!\n", (int) count);
 		IUCV_DBF_TEXT_(setup, 2,
 			       "%d is length of username\n", (int) count);
 		return -EINVAL;
@@ -1409,7 +1387,6 @@
 			/* trailing lf, grr */
 			break;
 		}
-		PRINT_WARN("netiucv: Invalid char %c in username!\n", *p);
 		IUCV_DBF_TEXT_(setup, 2,
 			       "username: invalid character %c\n", *p);
 		return -EINVAL;
@@ -1421,18 +1398,15 @@
 	if (memcmp(username, priv->conn->userid, 9) &&
 	    (ndev->flags & (IFF_UP | IFF_RUNNING))) {
 		/* username changed while the interface is active. */
-		PRINT_WARN("netiucv: device %s active, connected to %s\n",
-			   dev->bus_id, priv->conn->userid);
-		PRINT_WARN("netiucv: user cannot be updated\n");
 		IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
-		return -EBUSY;
+		return -EPERM;
 	}
 	read_lock_bh(&iucv_connection_rwlock);
 	list_for_each_entry(cp, &iucv_connection_list, list) {
 		if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
 			read_unlock_bh(&iucv_connection_rwlock);
-			PRINT_WARN("netiucv: Connection to %s already "
-				   "exists\n", username);
+			IUCV_DBF_TEXT_(setup, 2, "user_write: Connection "
+				"to %s already exists\n", username);
 			return -EEXIST;
 		}
 	}
@@ -1466,13 +1440,10 @@
 	bs1 = simple_strtoul(buf, &e, 0);
 
 	if (e && (!isspace(*e))) {
-		PRINT_WARN("netiucv: Invalid character in buffer!\n");
 		IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
 		return -EINVAL;
 	}
 	if (bs1 > NETIUCV_BUFSIZE_MAX) {
-		PRINT_WARN("netiucv: Given buffer size %d too large.\n",
-			bs1);
 		IUCV_DBF_TEXT_(setup, 2,
 			"buffer_write: buffer size %d too large\n",
 			bs1);
@@ -1480,16 +1451,12 @@
 	}
 	if ((ndev->flags & IFF_RUNNING) &&
 	    (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) {
-		PRINT_WARN("netiucv: Given buffer size %d too small.\n",
-			bs1);
 		IUCV_DBF_TEXT_(setup, 2,
 			"buffer_write: buffer size %d too small\n",
 			bs1);
 		return -EINVAL;
 	}
 	if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) {
-		PRINT_WARN("netiucv: Given buffer size %d too small.\n",
-			bs1);
 		IUCV_DBF_TEXT_(setup, 2,
 			"buffer_write: buffer size %d too small\n",
 			bs1);
@@ -1963,7 +1930,6 @@
 
 	IUCV_DBF_TEXT(trace, 3, __func__);
 	if (count>9) {
-		PRINT_WARN("netiucv: username too long (%d)!\n", (int)count);
 		IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
 		return -EINVAL;
 	}
@@ -1976,7 +1942,6 @@
 		if (*p == '\n')
 			/* trailing lf, grr */
 			break;
-		PRINT_WARN("netiucv: Invalid character in username!\n");
 		IUCV_DBF_TEXT_(setup, 2,
 			       "conn_write: invalid character %c\n", *p);
 		return -EINVAL;
@@ -1989,8 +1954,8 @@
 	list_for_each_entry(cp, &iucv_connection_list, list) {
 		if (!strncmp(username, cp->userid, 9)) {
 			read_unlock_bh(&iucv_connection_rwlock);
-			PRINT_WARN("netiucv: Connection to %s already "
-				   "exists\n", username);
+			IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection "
+				"to %s already exists\n", username);
 			return -EEXIST;
 		}
 	}
@@ -1998,9 +1963,6 @@
 
 	dev = netiucv_init_netdevice(username);
 	if (!dev) {
-		PRINT_WARN("netiucv: Could not allocate network device "
-			   "structure for user '%s'\n",
-			   netiucv_printname(username));
 		IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
 		return -ENODEV;
 	}
@@ -2020,15 +1982,12 @@
 	if (rc)
 		goto out_unreg;
 
-	PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username));
 
 	return count;
 
 out_unreg:
 	netiucv_unregister_device(priv->dev);
 out_free_ndev:
-	PRINT_WARN("netiucv: Could not register '%s'\n", dev->name);
-	IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n");
 	netiucv_free_netdevice(dev);
 	return rc;
 }
@@ -2073,14 +2032,13 @@
                         PRINT_WARN("netiucv: %s cannot be removed\n",
 				   ndev->name);
 			IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
-                        return -EBUSY;
+			return -EPERM;
                 }
                 unregister_netdev(ndev);
                 netiucv_unregister_device(dev);
                 return count;
         }
 	read_unlock_bh(&iucv_connection_rwlock);
-        PRINT_WARN("netiucv: net device %s unknown\n", name);
 	IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
         return -EINVAL;
 }
@@ -2148,7 +2106,6 @@
 	netiucv_driver.groups = netiucv_drv_attr_groups;
 	rc = driver_register(&netiucv_driver);
 	if (rc) {
-		PRINT_ERR("NETIUCV: failed to register driver.\n");
 		IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc);
 		goto out_iucv;
 	}
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 9a71dae..0ac54dc 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -420,7 +420,7 @@
 				QETH_DBF_TEXT(TRACE, 3, "urla");
 				break;
 			default:
-				PRINT_WARN("Received data is IPA "
+				QETH_DBF_MESSAGE(2, "Received data is IPA "
 					   "but not a reply!\n");
 				break;
 			}
@@ -735,8 +735,8 @@
 	char *sense;
 
 	sense = (char *) irb->ecw;
-	cstat = irb->scsw.cstat;
-	dstat = irb->scsw.dstat;
+	cstat = irb->scsw.cmd.cstat;
+	dstat = irb->scsw.cmd.dstat;
 
 	if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
 		     SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
@@ -823,8 +823,8 @@
 
 	if (__qeth_check_irb_error(cdev, intparm, irb))
 		return;
-	cstat = irb->scsw.cstat;
-	dstat = irb->scsw.dstat;
+	cstat = irb->scsw.cmd.cstat;
+	dstat = irb->scsw.cmd.dstat;
 
 	card = CARD_FROM_CDEV(cdev);
 	if (!card)
@@ -842,10 +842,10 @@
 	}
 	atomic_set(&channel->irq_pending, 0);
 
-	if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC))
+	if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC))
 		channel->state = CH_STATE_STOPPED;
 
-	if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC))
+	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC))
 		channel->state = CH_STATE_HALTED;
 
 	/*let's wake up immediately on data channel*/
@@ -4092,7 +4092,6 @@
 
 	rc = qeth_determine_card_type(card);
 	if (rc) {
-		PRINT_WARN("%s: not a valid card type\n", __func__);
 		QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
 		goto err_card;
 	}
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 999552c..06deaee 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -944,15 +944,8 @@
 	else
 		rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP,
 					addr->del_flags);
-	if (rc) {
+	if (rc)
 		QETH_DBF_TEXT(TRACE, 2, "failed");
-		/* TODO: re-activate this warning as soon as we have a
-		 * clean mirco code
-		qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
-		PRINT_WARN("Could not deregister IP address %s (rc=%x)\n",
-			   buf, rc);
-		*/
-	}
 
 	return rc;
 }
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 8735a41..164e090 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -156,11 +156,8 @@
 	if (rc != 0)
 		goto out;
 	rc = iucv_register(&smsg_handler, 1);
-	if (rc) {
-		printk(KERN_ERR "SMSGIUCV: failed to register to iucv");
-		rc = -EIO;	/* better errno ? */
+	if (rc)
 		goto out_driver;
-	}
 	smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL);
 	if (!smsg_path) {
 		rc = -ENOMEM;
@@ -168,11 +165,8 @@
 	}
 	rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG    ",
 			       NULL, NULL, NULL);
-	if (rc) {
-		printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG");
-		rc = -EIO;	/* better errno ? */
+	if (rc)
 		goto out_free;
-	}
 	cpcmd("SET SMSG IUCV", NULL, 0, NULL);
 	return 0;
 
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index 5bfbe76..834e9ee 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -2,10 +2,10 @@
  *  drivers/s390/s390mach.c
  *   S/390 machine check handler
  *
- *  S390 version
- *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Copyright IBM Corp. 2000,2008
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
  *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *		 Cornelia Huck <cornelia.huck@de.ibm.com>
  */
 
 #include <linux/init.h>
@@ -18,10 +18,6 @@
 #include <asm/etr.h>
 #include <asm/lowcore.h>
 #include <asm/cio.h>
-#include "cio/cio.h"
-#include "cio/chsc.h"
-#include "cio/css.h"
-#include "cio/chp.h"
 #include "s390mach.h"
 
 static struct semaphore m_sem;
@@ -36,13 +32,40 @@
 	for(;;);
 }
 
+static crw_handler_t crw_handlers[NR_RSCS];
+
+/**
+ * s390_register_crw_handler() - register a channel report word handler
+ * @rsc: reporting source code to handle
+ * @handler: handler to be registered
+ *
+ * Returns %0 on success and a negative error value otherwise.
+ */
+int s390_register_crw_handler(int rsc, crw_handler_t handler)
+{
+	if ((rsc < 0) || (rsc >= NR_RSCS))
+		return -EINVAL;
+	if (!cmpxchg(&crw_handlers[rsc], NULL, handler))
+		return 0;
+	return -EBUSY;
+}
+
+/**
+ * s390_unregister_crw_handler() - unregister a channel report word handler
+ * @rsc: reporting source code to handle
+ */
+void s390_unregister_crw_handler(int rsc)
+{
+	if ((rsc < 0) || (rsc >= NR_RSCS))
+		return;
+	xchg(&crw_handlers[rsc], NULL);
+	synchronize_sched();
+}
+
 /*
  * Retrieve CRWs and call function to handle event.
- *
- * Note : we currently process CRWs for io and chsc subchannels only
  */
-static int
-s390_collect_crw_info(void *param)
+static int s390_collect_crw_info(void *param)
 {
 	struct crw crw[2];
 	int ccode;
@@ -84,57 +107,24 @@
 		       crw[chain].rsid);
 		/* Check for overflows. */
 		if (crw[chain].oflw) {
+			int i;
+
 			pr_debug("%s: crw overflow detected!\n", __func__);
-			css_schedule_eval_all();
+			for (i = 0; i < NR_RSCS; i++) {
+				if (crw_handlers[i])
+					crw_handlers[i](NULL, NULL, 1);
+			}
 			chain = 0;
 			continue;
 		}
-		switch (crw[chain].rsc) {
-		case CRW_RSC_SCH:
-			if (crw[0].chn && !chain)
-				break;
-			pr_debug("source is subchannel %04X\n", crw[0].rsid);
-			css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
-			break;
-		case CRW_RSC_MONITOR:
-			pr_debug("source is monitoring facility\n");
-			break;
-		case CRW_RSC_CPATH:
-			pr_debug("source is channel path %02X\n", crw[0].rsid);
-			/*
-			 * Check for solicited machine checks. These are
-			 * created by reset channel path and need not be
-			 * reported to the common I/O layer.
-			 */
-			if (crw[chain].slct) {
-				pr_debug("solicited machine check for "
-					 "channel path %02X\n", crw[0].rsid);
-				break;
-			}
-			switch (crw[0].erc) {
-			case CRW_ERC_IPARM: /* Path has come. */
-				chp_process_crw(crw[0].rsid, 1);
-				break;
-			case CRW_ERC_PERRI: /* Path has gone. */
-			case CRW_ERC_PERRN:
-				chp_process_crw(crw[0].rsid, 0);
-				break;
-			default:
-				pr_debug("Don't know how to handle erc=%x\n",
-					 crw[0].erc);
-			}
-			break;
-		case CRW_RSC_CONFIG:
-			pr_debug("source is configuration-alert facility\n");
-			break;
-		case CRW_RSC_CSS:
-			pr_debug("source is channel subsystem\n");
-			chsc_process_crw();
-			break;
-		default:
-			pr_debug("unknown source\n");
-			break;
+		if (crw[0].chn && !chain) {
+			chain++;
+			continue;
 		}
+		if (crw_handlers[crw[chain].rsc])
+			crw_handlers[crw[chain].rsc](&crw[0],
+						     chain ? &crw[1] : NULL,
+						     0);
 		/* chain is always 0 or 1 here. */
 		chain = crw[chain].chn ? chain + 1 : 0;
 	}
@@ -468,6 +458,10 @@
 			etr_sync_check();
 		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
 			etr_switch_to_local();
+		if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC))
+			stp_sync_check();
+		if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND))
+			stp_island_check();
 	}
 
 	if (mci->se)
diff --git a/drivers/s390/s390mach.h b/drivers/s390/s390mach.h
index ca681f9..d39f8b6 100644
--- a/drivers/s390/s390mach.h
+++ b/drivers/s390/s390mach.h
@@ -72,6 +72,13 @@
 	__u32 rsid    : 16;   /* reporting-source ID */
 } __attribute__ ((packed));
 
+typedef void (*crw_handler_t)(struct crw *, struct crw *, int);
+
+extern int s390_register_crw_handler(int rsc, crw_handler_t handler);
+extern void s390_unregister_crw_handler(int rsc);
+
+#define NR_RSCS 16
+
 #define CRW_RSC_MONITOR  0x2  /* monitoring facility */
 #define CRW_RSC_SCH      0x3  /* subchannel */
 #define CRW_RSC_CPATH    0x4  /* channel path */
@@ -105,6 +112,9 @@
 #define ED_ETR_SYNC	12	/* External damage ETR sync check */
 #define ED_ETR_SWITCH	13	/* External damage ETR switch to local */
 
+#define ED_STP_SYNC	7	/* External damage STP sync check */
+#define ED_STP_ISLAND	6	/* External damage STP island check */
+
 struct pt_regs;
 
 void s390_handle_mcck(void);
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index ea0edd1..fe694f0 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -182,8 +182,9 @@
 			 int tablesize);
 static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,
 			   Sg_request * srp);
-static ssize_t sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count,
-			    int blocking, int read_only, Sg_request ** o_srp);
+static ssize_t sg_new_write(Sg_fd *sfp, struct file *file,
+			const char __user *buf, size_t count, int blocking,
+			int read_only, Sg_request **o_srp);
 static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
 			   unsigned char *cmnd, int timeout, int blocking);
 static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,
@@ -204,7 +205,6 @@
 static Sg_request *sg_add_request(Sg_fd * sfp);
 static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
 static int sg_res_in_use(Sg_fd * sfp);
-static int sg_allow_access(unsigned char opcode, char dev_type);
 static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len);
 static Sg_device *sg_get_dev(int dev);
 #ifdef CONFIG_SCSI_PROC_FS
@@ -544,7 +544,7 @@
 		return -EFAULT;
 	blocking = !(filp->f_flags & O_NONBLOCK);
 	if (old_hdr.reply_len < 0)
-		return sg_new_write(sfp, buf, count, blocking, 0, NULL);
+		return sg_new_write(sfp, filp, buf, count, blocking, 0, NULL);
 	if (count < (SZ_SG_HEADER + 6))
 		return -EIO;	/* The minimum scsi command length is 6 bytes. */
 
@@ -621,8 +621,9 @@
 }
 
 static ssize_t
-sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count,
-	     int blocking, int read_only, Sg_request ** o_srp)
+sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf,
+		 size_t count, int blocking, int read_only,
+		 Sg_request **o_srp)
 {
 	int k;
 	Sg_request *srp;
@@ -678,8 +679,7 @@
 		sg_remove_request(sfp, srp);
 		return -EFAULT;
 	}
-	if (read_only &&
-	    (!sg_allow_access(cmnd[0], sfp->parentdp->device->type))) {
+	if (read_only && !blk_verify_command(file, cmnd)) {
 		sg_remove_request(sfp, srp);
 		return -EPERM;
 	}
@@ -799,7 +799,7 @@
 			if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR))
 				return -EFAULT;
 			result =
-			    sg_new_write(sfp, p, SZ_SG_IO_HDR,
+			    sg_new_write(sfp, filp, p, SZ_SG_IO_HDR,
 					 blocking, read_only, &srp);
 			if (result < 0)
 				return result;
@@ -1048,7 +1048,7 @@
 
 			if (copy_from_user(&opcode, siocp->data, 1))
 				return -EFAULT;
-			if (!sg_allow_access(opcode, sdp->device->type))
+			if (!blk_verify_command(filp, &opcode))
 				return -EPERM;
 		}
 		return sg_scsi_ioctl(filp, sdp->device->request_queue, NULL, p);
@@ -2502,30 +2502,6 @@
 	__free_pages(page, order);
 }
 
-#ifndef MAINTENANCE_IN_CMD
-#define MAINTENANCE_IN_CMD 0xa3
-#endif
-
-static unsigned char allow_ops[] = { TEST_UNIT_READY, REQUEST_SENSE,
-	INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12,
-	READ_16, MODE_SENSE, MODE_SENSE_10, LOG_SENSE, REPORT_LUNS,
-	SERVICE_ACTION_IN, RECEIVE_DIAGNOSTIC, READ_LONG, MAINTENANCE_IN_CMD
-};
-
-static int
-sg_allow_access(unsigned char opcode, char dev_type)
-{
-	int k;
-
-	if (TYPE_SCANNER == dev_type)	/* TYPE_ROM maybe burner */
-		return 1;
-	for (k = 0; k < sizeof (allow_ops); ++k) {
-		if (opcode == allow_ops[k])
-			return 1;
-	}
-	return 0;
-}
-
 #ifdef CONFIG_SCSI_PROC_FS
 static int
 sg_idr_max_id(int id, void *p, void *data)
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index c82df8b..27f5bfd 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -673,24 +673,20 @@
 static void get_sectorsize(struct scsi_cd *cd)
 {
 	unsigned char cmd[10];
-	unsigned char *buffer;
+	unsigned char buffer[8];
 	int the_result, retries = 3;
 	int sector_size;
 	struct request_queue *queue;
 
-	buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
-	if (!buffer)
-		goto Enomem;
-
 	do {
 		cmd[0] = READ_CAPACITY;
 		memset((void *) &cmd[1], 0, 9);
-		memset(buffer, 0, 8);
+		memset(buffer, 0, sizeof(buffer));
 
 		/* Do the command and wait.. */
 		the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE,
-					      buffer, 8, NULL, SR_TIMEOUT,
-					      MAX_RETRIES);
+					      buffer, sizeof(buffer), NULL,
+					      SR_TIMEOUT, MAX_RETRIES);
 
 		retries--;
 
@@ -745,14 +741,8 @@
 
 	queue = cd->device->request_queue;
 	blk_queue_hardsect_size(queue, sector_size);
-out:
-	kfree(buffer);
-	return;
 
-Enomem:
-	cd->capacity = 0x1fffff;
-	cd->device->sector_size = 2048;	/* A guess, just in case */
-	goto out;
+	return;
 }
 
 static void get_capabilities(struct scsi_cd *cd)
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index 42be8b0..6aeef22b 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -1439,14 +1439,29 @@
 };
 
 #ifdef CONFIG_PM
+static bool atmel_serial_clk_will_stop(void)
+{
+#ifdef CONFIG_ARCH_AT91
+	return at91_suspend_entering_slow_clock();
+#else
+	return false;
+#endif
+}
+
 static int atmel_serial_suspend(struct platform_device *pdev,
 				pm_message_t state)
 {
 	struct uart_port *port = platform_get_drvdata(pdev);
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
+	if (atmel_is_console_port(port) && console_suspend_enabled) {
+		/* Drain the TX shifter */
+		while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY))
+			cpu_relax();
+	}
+
 	if (device_may_wakeup(&pdev->dev)
-	    && !at91_suspend_entering_slow_clock())
+	    && !atmel_serial_clk_will_stop())
 		enable_irq_wake(port->irq);
 	else {
 		uart_suspend_port(&atmel_uart, port);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index e0c5f96..9b887ef 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -7,7 +7,7 @@
 
 source "drivers/char/agp/Kconfig"
 
-source "drivers/char/drm/Kconfig"
+source "drivers/gpu/drm/Kconfig"
 
 config VGASTATE
        tristate
diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c
index 0f86b0f..9678b3e 100644
--- a/drivers/xen/xenbus/xenbus_client.c
+++ b/drivers/xen/xenbus/xenbus_client.c
@@ -117,7 +117,7 @@
 	char *path;
 
 	va_start(ap, pathfmt);
-	path = kvasprintf(GFP_KERNEL, pathfmt, ap);
+	path = kvasprintf(GFP_NOIO | __GFP_HIGH, pathfmt, ap);
 	va_end(ap);
 
 	if (!path) {
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index 227d53b..7f2f91c 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -283,9 +283,9 @@
 	char *buffer;
 
 	if (strlen(name) == 0)
-		buffer = kasprintf(GFP_KERNEL, "%s", dir);
+		buffer = kasprintf(GFP_NOIO | __GFP_HIGH, "%s", dir);
 	else
-		buffer = kasprintf(GFP_KERNEL, "%s/%s", dir, name);
+		buffer = kasprintf(GFP_NOIO | __GFP_HIGH, "%s/%s", dir, name);
 	return (!buffer) ? ERR_PTR(-ENOMEM) : buffer;
 }
 
@@ -297,7 +297,7 @@
 	*num = count_strings(strings, len);
 
 	/* Transfer to one big alloc for easy freeing. */
-	ret = kmalloc(*num * sizeof(char *) + len, GFP_KERNEL);
+	ret = kmalloc(*num * sizeof(char *) + len, GFP_NOIO | __GFP_HIGH);
 	if (!ret) {
 		kfree(strings);
 		return ERR_PTR(-ENOMEM);
@@ -751,7 +751,7 @@
 	}
 
 
-	msg = kmalloc(sizeof(*msg), GFP_KERNEL);
+	msg = kmalloc(sizeof(*msg), GFP_NOIO | __GFP_HIGH);
 	if (msg == NULL) {
 		err = -ENOMEM;
 		goto out;
@@ -763,7 +763,7 @@
 		goto out;
 	}
 
-	body = kmalloc(msg->hdr.len + 1, GFP_KERNEL);
+	body = kmalloc(msg->hdr.len + 1, GFP_NOIO | __GFP_HIGH);
 	if (body == NULL) {
 		kfree(msg);
 		err = -ENOMEM;
diff --git a/fs/Makefile b/fs/Makefile
index 1e7a11b..277b079 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -19,6 +19,7 @@
 obj-y +=	no-block.o
 endif
 
+obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o
 obj-$(CONFIG_INOTIFY)		+= inotify.o
 obj-$(CONFIG_INOTIFY_USER)	+= inotify_user.o
 obj-$(CONFIG_EPOLL)		+= eventpoll.o
diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c
new file mode 100644
index 0000000..63e2ee6
--- /dev/null
+++ b/fs/bio-integrity.c
@@ -0,0 +1,719 @@
+/*
+ * bio-integrity.c - bio data integrity extensions
+ *
+ * Copyright (C) 2007, 2008 Oracle Corporation
+ * Written by: Martin K. Petersen <martin.petersen@oracle.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/mempool.h>
+#include <linux/bio.h>
+#include <linux/workqueue.h>
+
+static struct kmem_cache *bio_integrity_slab __read_mostly;
+static struct workqueue_struct *kintegrityd_wq;
+
+/**
+ * bio_integrity_alloc_bioset - Allocate integrity payload and attach it to bio
+ * @bio:	bio to attach integrity metadata to
+ * @gfp_mask:	Memory allocation mask
+ * @nr_vecs:	Number of integrity metadata scatter-gather elements
+ * @bs:		bio_set to allocate from
+ *
+ * Description: This function prepares a bio for attaching integrity
+ * metadata.  nr_vecs specifies the maximum number of pages containing
+ * integrity metadata that can be attached.
+ */
+struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *bio,
+							 gfp_t gfp_mask,
+							 unsigned int nr_vecs,
+							 struct bio_set *bs)
+{
+	struct bio_integrity_payload *bip;
+	struct bio_vec *iv;
+	unsigned long idx;
+
+	BUG_ON(bio == NULL);
+
+	bip = mempool_alloc(bs->bio_integrity_pool, gfp_mask);
+	if (unlikely(bip == NULL)) {
+		printk(KERN_ERR "%s: could not alloc bip\n", __func__);
+		return NULL;
+	}
+
+	memset(bip, 0, sizeof(*bip));
+
+	iv = bvec_alloc_bs(gfp_mask, nr_vecs, &idx, bs);
+	if (unlikely(iv == NULL)) {
+		printk(KERN_ERR "%s: could not alloc bip_vec\n", __func__);
+		mempool_free(bip, bs->bio_integrity_pool);
+		return NULL;
+	}
+
+	bip->bip_pool = idx;
+	bip->bip_vec = iv;
+	bip->bip_bio = bio;
+	bio->bi_integrity = bip;
+
+	return bip;
+}
+EXPORT_SYMBOL(bio_integrity_alloc_bioset);
+
+/**
+ * bio_integrity_alloc - Allocate integrity payload and attach it to bio
+ * @bio:	bio to attach integrity metadata to
+ * @gfp_mask:	Memory allocation mask
+ * @nr_vecs:	Number of integrity metadata scatter-gather elements
+ *
+ * Description: This function prepares a bio for attaching integrity
+ * metadata.  nr_vecs specifies the maximum number of pages containing
+ * integrity metadata that can be attached.
+ */
+struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio,
+						  gfp_t gfp_mask,
+						  unsigned int nr_vecs)
+{
+	return bio_integrity_alloc_bioset(bio, gfp_mask, nr_vecs, fs_bio_set);
+}
+EXPORT_SYMBOL(bio_integrity_alloc);
+
+/**
+ * bio_integrity_free - Free bio integrity payload
+ * @bio:	bio containing bip to be freed
+ * @bs:		bio_set this bio was allocated from
+ *
+ * Description: Used to free the integrity portion of a bio. Usually
+ * called from bio_free().
+ */
+void bio_integrity_free(struct bio *bio, struct bio_set *bs)
+{
+	struct bio_integrity_payload *bip = bio->bi_integrity;
+
+	BUG_ON(bip == NULL);
+
+	/* A cloned bio doesn't own the integrity metadata */
+	if (!bio_flagged(bio, BIO_CLONED) && bip->bip_buf != NULL)
+		kfree(bip->bip_buf);
+
+	mempool_free(bip->bip_vec, bs->bvec_pools[bip->bip_pool]);
+	mempool_free(bip, bs->bio_integrity_pool);
+
+	bio->bi_integrity = NULL;
+}
+EXPORT_SYMBOL(bio_integrity_free);
+
+/**
+ * bio_integrity_add_page - Attach integrity metadata
+ * @bio:	bio to update
+ * @page:	page containing integrity metadata
+ * @len:	number of bytes of integrity metadata in page
+ * @offset:	start offset within page
+ *
+ * Description: Attach a page containing integrity metadata to bio.
+ */
+int bio_integrity_add_page(struct bio *bio, struct page *page,
+			   unsigned int len, unsigned int offset)
+{
+	struct bio_integrity_payload *bip = bio->bi_integrity;
+	struct bio_vec *iv;
+
+	if (bip->bip_vcnt >= bvec_nr_vecs(bip->bip_pool)) {
+		printk(KERN_ERR "%s: bip_vec full\n", __func__);
+		return 0;
+	}
+
+	iv = bip_vec_idx(bip, bip->bip_vcnt);
+	BUG_ON(iv == NULL);
+	BUG_ON(iv->bv_page != NULL);
+
+	iv->bv_page = page;
+	iv->bv_len = len;
+	iv->bv_offset = offset;
+	bip->bip_vcnt++;
+
+	return len;
+}
+EXPORT_SYMBOL(bio_integrity_add_page);
+
+/**
+ * bio_integrity_enabled - Check whether integrity can be passed
+ * @bio:	bio to check
+ *
+ * Description: Determines whether bio_integrity_prep() can be called
+ * on this bio or not.	bio data direction and target device must be
+ * set prior to calling.  The functions honors the write_generate and
+ * read_verify flags in sysfs.
+ */
+int bio_integrity_enabled(struct bio *bio)
+{
+	/* Already protected? */
+	if (bio_integrity(bio))
+		return 0;
+
+	return bdev_integrity_enabled(bio->bi_bdev, bio_data_dir(bio));
+}
+EXPORT_SYMBOL(bio_integrity_enabled);
+
+/**
+ * bio_integrity_hw_sectors - Convert 512b sectors to hardware ditto
+ * @bi:		blk_integrity profile for device
+ * @sectors:	Number of 512 sectors to convert
+ *
+ * Description: The block layer calculates everything in 512 byte
+ * sectors but integrity metadata is done in terms of the hardware
+ * sector size of the storage device.  Convert the block layer sectors
+ * to physical sectors.
+ */
+static inline unsigned int bio_integrity_hw_sectors(struct blk_integrity *bi,
+						    unsigned int sectors)
+{
+	/* At this point there are only 512b or 4096b DIF/EPP devices */
+	if (bi->sector_size == 4096)
+		return sectors >>= 3;
+
+	return sectors;
+}
+
+/**
+ * bio_integrity_tag_size - Retrieve integrity tag space
+ * @bio:	bio to inspect
+ *
+ * Description: Returns the maximum number of tag bytes that can be
+ * attached to this bio. Filesystems can use this to determine how
+ * much metadata to attach to an I/O.
+ */
+unsigned int bio_integrity_tag_size(struct bio *bio)
+{
+	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
+
+	BUG_ON(bio->bi_size == 0);
+
+	return bi->tag_size * (bio->bi_size / bi->sector_size);
+}
+EXPORT_SYMBOL(bio_integrity_tag_size);
+
+int bio_integrity_tag(struct bio *bio, void *tag_buf, unsigned int len, int set)
+{
+	struct bio_integrity_payload *bip = bio->bi_integrity;
+	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
+	unsigned int nr_sectors;
+
+	BUG_ON(bip->bip_buf == NULL);
+
+	if (bi->tag_size == 0)
+		return -1;
+
+	nr_sectors = bio_integrity_hw_sectors(bi,
+					DIV_ROUND_UP(len, bi->tag_size));
+
+	if (nr_sectors * bi->tuple_size > bip->bip_size) {
+		printk(KERN_ERR "%s: tag too big for bio: %u > %u\n",
+		       __func__, nr_sectors * bi->tuple_size, bip->bip_size);
+		return -1;
+	}
+
+	if (set)
+		bi->set_tag_fn(bip->bip_buf, tag_buf, nr_sectors);
+	else
+		bi->get_tag_fn(bip->bip_buf, tag_buf, nr_sectors);
+
+	return 0;
+}
+
+/**
+ * bio_integrity_set_tag - Attach a tag buffer to a bio
+ * @bio:	bio to attach buffer to
+ * @tag_buf:	Pointer to a buffer containing tag data
+ * @len:	Length of the included buffer
+ *
+ * Description: Use this function to tag a bio by leveraging the extra
+ * space provided by devices formatted with integrity protection.  The
+ * size of the integrity buffer must be <= to the size reported by
+ * bio_integrity_tag_size().
+ */
+int bio_integrity_set_tag(struct bio *bio, void *tag_buf, unsigned int len)
+{
+	BUG_ON(bio_data_dir(bio) != WRITE);
+
+	return bio_integrity_tag(bio, tag_buf, len, 1);
+}
+EXPORT_SYMBOL(bio_integrity_set_tag);
+
+/**
+ * bio_integrity_get_tag - Retrieve a tag buffer from a bio
+ * @bio:	bio to retrieve buffer from
+ * @tag_buf:	Pointer to a buffer for the tag data
+ * @len:	Length of the target buffer
+ *
+ * Description: Use this function to retrieve the tag buffer from a
+ * completed I/O. The size of the integrity buffer must be <= to the
+ * size reported by bio_integrity_tag_size().
+ */
+int bio_integrity_get_tag(struct bio *bio, void *tag_buf, unsigned int len)
+{
+	BUG_ON(bio_data_dir(bio) != READ);
+
+	return bio_integrity_tag(bio, tag_buf, len, 0);
+}
+EXPORT_SYMBOL(bio_integrity_get_tag);
+
+/**
+ * bio_integrity_generate - Generate integrity metadata for a bio
+ * @bio:	bio to generate integrity metadata for
+ *
+ * Description: Generates integrity metadata for a bio by calling the
+ * block device's generation callback function.  The bio must have a
+ * bip attached with enough room to accommodate the generated
+ * integrity metadata.
+ */
+static void bio_integrity_generate(struct bio *bio)
+{
+	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
+	struct blk_integrity_exchg bix;
+	struct bio_vec *bv;
+	sector_t sector = bio->bi_sector;
+	unsigned int i, sectors, total;
+	void *prot_buf = bio->bi_integrity->bip_buf;
+
+	total = 0;
+	bix.disk_name = bio->bi_bdev->bd_disk->disk_name;
+	bix.sector_size = bi->sector_size;
+
+	bio_for_each_segment(bv, bio, i) {
+		void *kaddr = kmap_atomic(bv->bv_page, KM_USER0);
+		bix.data_buf = kaddr + bv->bv_offset;
+		bix.data_size = bv->bv_len;
+		bix.prot_buf = prot_buf;
+		bix.sector = sector;
+
+		bi->generate_fn(&bix);
+
+		sectors = bv->bv_len / bi->sector_size;
+		sector += sectors;
+		prot_buf += sectors * bi->tuple_size;
+		total += sectors * bi->tuple_size;
+		BUG_ON(total > bio->bi_integrity->bip_size);
+
+		kunmap_atomic(kaddr, KM_USER0);
+	}
+}
+
+/**
+ * bio_integrity_prep - Prepare bio for integrity I/O
+ * @bio:	bio to prepare
+ *
+ * Description: Allocates a buffer for integrity metadata, maps the
+ * pages and attaches them to a bio.  The bio must have data
+ * direction, target device and start sector set priot to calling.  In
+ * the WRITE case, integrity metadata will be generated using the
+ * block device's integrity function.  In the READ case, the buffer
+ * will be prepared for DMA and a suitable end_io handler set up.
+ */
+int bio_integrity_prep(struct bio *bio)
+{
+	struct bio_integrity_payload *bip;
+	struct blk_integrity *bi;
+	struct request_queue *q;
+	void *buf;
+	unsigned long start, end;
+	unsigned int len, nr_pages;
+	unsigned int bytes, offset, i;
+	unsigned int sectors;
+
+	bi = bdev_get_integrity(bio->bi_bdev);
+	q = bdev_get_queue(bio->bi_bdev);
+	BUG_ON(bi == NULL);
+	BUG_ON(bio_integrity(bio));
+
+	sectors = bio_integrity_hw_sectors(bi, bio_sectors(bio));
+
+	/* Allocate kernel buffer for protection data */
+	len = sectors * blk_integrity_tuple_size(bi);
+	buf = kmalloc(len, GFP_NOIO | __GFP_NOFAIL | q->bounce_gfp);
+	if (unlikely(buf == NULL)) {
+		printk(KERN_ERR "could not allocate integrity buffer\n");
+		return -EIO;
+	}
+
+	end = (((unsigned long) buf) + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	start = ((unsigned long) buf) >> PAGE_SHIFT;
+	nr_pages = end - start;
+
+	/* Allocate bio integrity payload and integrity vectors */
+	bip = bio_integrity_alloc(bio, GFP_NOIO, nr_pages);
+	if (unlikely(bip == NULL)) {
+		printk(KERN_ERR "could not allocate data integrity bioset\n");
+		kfree(buf);
+		return -EIO;
+	}
+
+	bip->bip_buf = buf;
+	bip->bip_size = len;
+	bip->bip_sector = bio->bi_sector;
+
+	/* Map it */
+	offset = offset_in_page(buf);
+	for (i = 0 ; i < nr_pages ; i++) {
+		int ret;
+		bytes = PAGE_SIZE - offset;
+
+		if (len <= 0)
+			break;
+
+		if (bytes > len)
+			bytes = len;
+
+		ret = bio_integrity_add_page(bio, virt_to_page(buf),
+					     bytes, offset);
+
+		if (ret == 0)
+			return 0;
+
+		if (ret < bytes)
+			break;
+
+		buf += bytes;
+		len -= bytes;
+		offset = 0;
+	}
+
+	/* Install custom I/O completion handler if read verify is enabled */
+	if (bio_data_dir(bio) == READ) {
+		bip->bip_end_io = bio->bi_end_io;
+		bio->bi_end_io = bio_integrity_endio;
+	}
+
+	/* Auto-generate integrity metadata if this is a write */
+	if (bio_data_dir(bio) == WRITE)
+		bio_integrity_generate(bio);
+
+	return 0;
+}
+EXPORT_SYMBOL(bio_integrity_prep);
+
+/**
+ * bio_integrity_verify - Verify integrity metadata for a bio
+ * @bio:	bio to verify
+ *
+ * Description: This function is called to verify the integrity of a
+ * bio.	 The data in the bio io_vec is compared to the integrity
+ * metadata returned by the HBA.
+ */
+static int bio_integrity_verify(struct bio *bio)
+{
+	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
+	struct blk_integrity_exchg bix;
+	struct bio_vec *bv;
+	sector_t sector = bio->bi_integrity->bip_sector;
+	unsigned int i, sectors, total, ret;
+	void *prot_buf = bio->bi_integrity->bip_buf;
+
+	ret = total = 0;
+	bix.disk_name = bio->bi_bdev->bd_disk->disk_name;
+	bix.sector_size = bi->sector_size;
+
+	bio_for_each_segment(bv, bio, i) {
+		void *kaddr = kmap_atomic(bv->bv_page, KM_USER0);
+		bix.data_buf = kaddr + bv->bv_offset;
+		bix.data_size = bv->bv_len;
+		bix.prot_buf = prot_buf;
+		bix.sector = sector;
+
+		ret = bi->verify_fn(&bix);
+
+		if (ret) {
+			kunmap_atomic(kaddr, KM_USER0);
+			break;
+		}
+
+		sectors = bv->bv_len / bi->sector_size;
+		sector += sectors;
+		prot_buf += sectors * bi->tuple_size;
+		total += sectors * bi->tuple_size;
+		BUG_ON(total > bio->bi_integrity->bip_size);
+
+		kunmap_atomic(kaddr, KM_USER0);
+	}
+
+	return ret;
+}
+
+/**
+ * bio_integrity_verify_fn - Integrity I/O completion worker
+ * @work:	Work struct stored in bio to be verified
+ *
+ * Description: This workqueue function is called to complete a READ
+ * request.  The function verifies the transferred integrity metadata
+ * and then calls the original bio end_io function.
+ */
+static void bio_integrity_verify_fn(struct work_struct *work)
+{
+	struct bio_integrity_payload *bip =
+		container_of(work, struct bio_integrity_payload, bip_work);
+	struct bio *bio = bip->bip_bio;
+	int error = bip->bip_error;
+
+	if (bio_integrity_verify(bio)) {
+		clear_bit(BIO_UPTODATE, &bio->bi_flags);
+		error = -EIO;
+	}
+
+	/* Restore original bio completion handler */
+	bio->bi_end_io = bip->bip_end_io;
+
+	if (bio->bi_end_io)
+		bio->bi_end_io(bio, error);
+}
+
+/**
+ * bio_integrity_endio - Integrity I/O completion function
+ * @bio:	Protected bio
+ * @error:	Pointer to errno
+ *
+ * Description: Completion for integrity I/O
+ *
+ * Normally I/O completion is done in interrupt context.  However,
+ * verifying I/O integrity is a time-consuming task which must be run
+ * in process context.	This function postpones completion
+ * accordingly.
+ */
+void bio_integrity_endio(struct bio *bio, int error)
+{
+	struct bio_integrity_payload *bip = bio->bi_integrity;
+
+	BUG_ON(bip->bip_bio != bio);
+
+	bip->bip_error = error;
+	INIT_WORK(&bip->bip_work, bio_integrity_verify_fn);
+	queue_work(kintegrityd_wq, &bip->bip_work);
+}
+EXPORT_SYMBOL(bio_integrity_endio);
+
+/**
+ * bio_integrity_mark_head - Advance bip_vec skip bytes
+ * @bip:	Integrity vector to advance
+ * @skip:	Number of bytes to advance it
+ */
+void bio_integrity_mark_head(struct bio_integrity_payload *bip,
+			     unsigned int skip)
+{
+	struct bio_vec *iv;
+	unsigned int i;
+
+	bip_for_each_vec(iv, bip, i) {
+		if (skip == 0) {
+			bip->bip_idx = i;
+			return;
+		} else if (skip >= iv->bv_len) {
+			skip -= iv->bv_len;
+		} else { /* skip < iv->bv_len) */
+			iv->bv_offset += skip;
+			iv->bv_len -= skip;
+			bip->bip_idx = i;
+			return;
+		}
+	}
+}
+
+/**
+ * bio_integrity_mark_tail - Truncate bip_vec to be len bytes long
+ * @bip:	Integrity vector to truncate
+ * @len:	New length of integrity vector
+ */
+void bio_integrity_mark_tail(struct bio_integrity_payload *bip,
+			     unsigned int len)
+{
+	struct bio_vec *iv;
+	unsigned int i;
+
+	bip_for_each_vec(iv, bip, i) {
+		if (len == 0) {
+			bip->bip_vcnt = i;
+			return;
+		} else if (len >= iv->bv_len) {
+			len -= iv->bv_len;
+		} else { /* len < iv->bv_len) */
+			iv->bv_len = len;
+			len = 0;
+		}
+	}
+}
+
+/**
+ * bio_integrity_advance - Advance integrity vector
+ * @bio:	bio whose integrity vector to update
+ * @bytes_done:	number of data bytes that have been completed
+ *
+ * Description: This function calculates how many integrity bytes the
+ * number of completed data bytes correspond to and advances the
+ * integrity vector accordingly.
+ */
+void bio_integrity_advance(struct bio *bio, unsigned int bytes_done)
+{
+	struct bio_integrity_payload *bip = bio->bi_integrity;
+	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
+	unsigned int nr_sectors;
+
+	BUG_ON(bip == NULL);
+	BUG_ON(bi == NULL);
+
+	nr_sectors = bio_integrity_hw_sectors(bi, bytes_done >> 9);
+	bio_integrity_mark_head(bip, nr_sectors * bi->tuple_size);
+}
+EXPORT_SYMBOL(bio_integrity_advance);
+
+/**
+ * bio_integrity_trim - Trim integrity vector
+ * @bio:	bio whose integrity vector to update
+ * @offset:	offset to first data sector
+ * @sectors:	number of data sectors
+ *
+ * Description: Used to trim the integrity vector in a cloned bio.
+ * The ivec will be advanced corresponding to 'offset' data sectors
+ * and the length will be truncated corresponding to 'len' data
+ * sectors.
+ */
+void bio_integrity_trim(struct bio *bio, unsigned int offset,
+			unsigned int sectors)
+{
+	struct bio_integrity_payload *bip = bio->bi_integrity;
+	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
+	unsigned int nr_sectors;
+
+	BUG_ON(bip == NULL);
+	BUG_ON(bi == NULL);
+	BUG_ON(!bio_flagged(bio, BIO_CLONED));
+
+	nr_sectors = bio_integrity_hw_sectors(bi, sectors);
+	bip->bip_sector = bip->bip_sector + offset;
+	bio_integrity_mark_head(bip, offset * bi->tuple_size);
+	bio_integrity_mark_tail(bip, sectors * bi->tuple_size);
+}
+EXPORT_SYMBOL(bio_integrity_trim);
+
+/**
+ * bio_integrity_split - Split integrity metadata
+ * @bio:	Protected bio
+ * @bp:		Resulting bio_pair
+ * @sectors:	Offset
+ *
+ * Description: Splits an integrity page into a bio_pair.
+ */
+void bio_integrity_split(struct bio *bio, struct bio_pair *bp, int sectors)
+{
+	struct blk_integrity *bi;
+	struct bio_integrity_payload *bip = bio->bi_integrity;
+	unsigned int nr_sectors;
+
+	if (bio_integrity(bio) == 0)
+		return;
+
+	bi = bdev_get_integrity(bio->bi_bdev);
+	BUG_ON(bi == NULL);
+	BUG_ON(bip->bip_vcnt != 1);
+
+	nr_sectors = bio_integrity_hw_sectors(bi, sectors);
+
+	bp->bio1.bi_integrity = &bp->bip1;
+	bp->bio2.bi_integrity = &bp->bip2;
+
+	bp->iv1 = bip->bip_vec[0];
+	bp->iv2 = bip->bip_vec[0];
+
+	bp->bip1.bip_vec = &bp->iv1;
+	bp->bip2.bip_vec = &bp->iv2;
+
+	bp->iv1.bv_len = sectors * bi->tuple_size;
+	bp->iv2.bv_offset += sectors * bi->tuple_size;
+	bp->iv2.bv_len -= sectors * bi->tuple_size;
+
+	bp->bip1.bip_sector = bio->bi_integrity->bip_sector;
+	bp->bip2.bip_sector = bio->bi_integrity->bip_sector + nr_sectors;
+
+	bp->bip1.bip_vcnt = bp->bip2.bip_vcnt = 1;
+	bp->bip1.bip_idx = bp->bip2.bip_idx = 0;
+}
+EXPORT_SYMBOL(bio_integrity_split);
+
+/**
+ * bio_integrity_clone - Callback for cloning bios with integrity metadata
+ * @bio:	New bio
+ * @bio_src:	Original bio
+ * @bs:		bio_set to allocate bip from
+ *
+ * Description:	Called to allocate a bip when cloning a bio
+ */
+int bio_integrity_clone(struct bio *bio, struct bio *bio_src,
+			struct bio_set *bs)
+{
+	struct bio_integrity_payload *bip_src = bio_src->bi_integrity;
+	struct bio_integrity_payload *bip;
+
+	BUG_ON(bip_src == NULL);
+
+	bip = bio_integrity_alloc_bioset(bio, GFP_NOIO, bip_src->bip_vcnt, bs);
+
+	if (bip == NULL)
+		return -EIO;
+
+	memcpy(bip->bip_vec, bip_src->bip_vec,
+	       bip_src->bip_vcnt * sizeof(struct bio_vec));
+
+	bip->bip_sector = bip_src->bip_sector;
+	bip->bip_vcnt = bip_src->bip_vcnt;
+	bip->bip_idx = bip_src->bip_idx;
+
+	return 0;
+}
+EXPORT_SYMBOL(bio_integrity_clone);
+
+int bioset_integrity_create(struct bio_set *bs, int pool_size)
+{
+	bs->bio_integrity_pool = mempool_create_slab_pool(pool_size,
+							  bio_integrity_slab);
+	if (!bs->bio_integrity_pool)
+		return -1;
+
+	return 0;
+}
+EXPORT_SYMBOL(bioset_integrity_create);
+
+void bioset_integrity_free(struct bio_set *bs)
+{
+	if (bs->bio_integrity_pool)
+		mempool_destroy(bs->bio_integrity_pool);
+}
+EXPORT_SYMBOL(bioset_integrity_free);
+
+void __init bio_integrity_init_slab(void)
+{
+	bio_integrity_slab = KMEM_CACHE(bio_integrity_payload,
+					SLAB_HWCACHE_ALIGN|SLAB_PANIC);
+}
+EXPORT_SYMBOL(bio_integrity_init_slab);
+
+static int __init integrity_init(void)
+{
+	kintegrityd_wq = create_workqueue("kintegrityd");
+
+	if (!kintegrityd_wq)
+		panic("Failed to create kintegrityd\n");
+
+	return 0;
+}
+subsys_initcall(integrity_init);
diff --git a/fs/bio.c b/fs/bio.c
index 7856257..88322b0 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -28,25 +28,10 @@
 #include <linux/blktrace_api.h>
 #include <scsi/sg.h>		/* for struct sg_iovec */
 
-#define BIO_POOL_SIZE 2
-
 static struct kmem_cache *bio_slab __read_mostly;
 
-#define BIOVEC_NR_POOLS 6
-
-/*
- * a small number of entries is fine, not going to be performance critical.
- * basically we just need to survive
- */
-#define BIO_SPLIT_ENTRIES 2
 mempool_t *bio_split_pool __read_mostly;
 
-struct biovec_slab {
-	int nr_vecs;
-	char *name; 
-	struct kmem_cache *slab;
-};
-
 /*
  * if you change this list, also change bvec_alloc or things will
  * break badly! cannot be bigger than what you can fit into an
@@ -60,23 +45,17 @@
 #undef BV
 
 /*
- * bio_set is used to allow other portions of the IO system to
- * allocate their own private memory pools for bio and iovec structures.
- * These memory pools in turn all allocate from the bio_slab
- * and the bvec_slabs[].
- */
-struct bio_set {
-	mempool_t *bio_pool;
-	mempool_t *bvec_pools[BIOVEC_NR_POOLS];
-};
-
-/*
  * fs_bio_set is the bio_set containing bio and iovec memory pools used by
  * IO code that does not need private memory pools.
  */
-static struct bio_set *fs_bio_set;
+struct bio_set *fs_bio_set;
 
-static inline struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs)
+unsigned int bvec_nr_vecs(unsigned short idx)
+{
+	return bvec_slabs[idx].nr_vecs;
+}
+
+struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs)
 {
 	struct bio_vec *bvl;
 
@@ -117,6 +96,9 @@
 		mempool_free(bio->bi_io_vec, bio_set->bvec_pools[pool_idx]);
 	}
 
+	if (bio_integrity(bio))
+		bio_integrity_free(bio, bio_set);
+
 	mempool_free(bio, bio_set->bio_pool);
 }
 
@@ -275,9 +257,19 @@
 {
 	struct bio *b = bio_alloc_bioset(gfp_mask, bio->bi_max_vecs, fs_bio_set);
 
-	if (b) {
-		b->bi_destructor = bio_fs_destructor;
-		__bio_clone(b, bio);
+	if (!b)
+		return NULL;
+
+	b->bi_destructor = bio_fs_destructor;
+	__bio_clone(b, bio);
+
+	if (bio_integrity(bio)) {
+		int ret;
+
+		ret = bio_integrity_clone(b, bio, fs_bio_set);
+
+		if (ret < 0)
+			return NULL;
 	}
 
 	return b;
@@ -333,10 +325,19 @@
 		if (page == prev->bv_page &&
 		    offset == prev->bv_offset + prev->bv_len) {
 			prev->bv_len += len;
-			if (q->merge_bvec_fn &&
-			    q->merge_bvec_fn(q, bio, prev) < len) {
-				prev->bv_len -= len;
-				return 0;
+
+			if (q->merge_bvec_fn) {
+				struct bvec_merge_data bvm = {
+					.bi_bdev = bio->bi_bdev,
+					.bi_sector = bio->bi_sector,
+					.bi_size = bio->bi_size,
+					.bi_rw = bio->bi_rw,
+				};
+
+				if (q->merge_bvec_fn(q, &bvm, prev) < len) {
+					prev->bv_len -= len;
+					return 0;
+				}
 			}
 
 			goto done;
@@ -377,11 +378,18 @@
 	 * queue to get further control
 	 */
 	if (q->merge_bvec_fn) {
+		struct bvec_merge_data bvm = {
+			.bi_bdev = bio->bi_bdev,
+			.bi_sector = bio->bi_sector,
+			.bi_size = bio->bi_size,
+			.bi_rw = bio->bi_rw,
+		};
+
 		/*
 		 * merge_bvec_fn() returns number of bytes it can accept
 		 * at this offset
 		 */
-		if (q->merge_bvec_fn(q, bio, bvec) < len) {
+		if (q->merge_bvec_fn(q, &bvm, bvec) < len) {
 			bvec->bv_page = NULL;
 			bvec->bv_len = 0;
 			bvec->bv_offset = 0;
@@ -1249,6 +1257,9 @@
 	bp->bio1.bi_private = bi;
 	bp->bio2.bi_private = pool;
 
+	if (bio_integrity(bi))
+		bio_integrity_split(bi, bp, first_sectors);
+
 	return bp;
 }
 
@@ -1290,6 +1301,7 @@
 	if (bs->bio_pool)
 		mempool_destroy(bs->bio_pool);
 
+	bioset_integrity_free(bs);
 	biovec_free_pools(bs);
 
 	kfree(bs);
@@ -1306,6 +1318,9 @@
 	if (!bs->bio_pool)
 		goto bad;
 
+	if (bioset_integrity_create(bs, bio_pool_size))
+		goto bad;
+
 	if (!biovec_create_pools(bs, bvec_pool_size))
 		return bs;
 
@@ -1332,6 +1347,7 @@
 {
 	bio_slab = KMEM_CACHE(bio, SLAB_HWCACHE_ALIGN|SLAB_PANIC);
 
+	bio_integrity_init_slab();
 	biovec_init_slabs();
 
 	fs_bio_set = bioset_create(BIO_POOL_SIZE, 2);
diff --git a/fs/namespace.c b/fs/namespace.c
index 4fc302c..4f6f763 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -750,7 +750,7 @@
 	const char *str;
 };
 
-static void show_sb_opts(struct seq_file *m, struct super_block *sb)
+static int show_sb_opts(struct seq_file *m, struct super_block *sb)
 {
 	static const struct proc_fs_info fs_info[] = {
 		{ MS_SYNCHRONOUS, ",sync" },
@@ -764,6 +764,8 @@
 		if (sb->s_flags & fs_infop->flag)
 			seq_puts(m, fs_infop->str);
 	}
+
+	return security_sb_show_options(m, sb);
 }
 
 static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
@@ -806,11 +808,14 @@
 	seq_putc(m, ' ');
 	show_type(m, mnt->mnt_sb);
 	seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
-	show_sb_opts(m, mnt->mnt_sb);
+	err = show_sb_opts(m, mnt->mnt_sb);
+	if (err)
+		goto out;
 	show_mnt_opts(m, mnt);
 	if (mnt->mnt_sb->s_op->show_options)
 		err = mnt->mnt_sb->s_op->show_options(m, mnt);
 	seq_puts(m, " 0 0\n");
+out:
 	return err;
 }
 
@@ -865,10 +870,13 @@
 	seq_putc(m, ' ');
 	mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
 	seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw");
-	show_sb_opts(m, sb);
+	err = show_sb_opts(m, sb);
+	if (err)
+		goto out;
 	if (sb->s_op->show_options)
 		err = sb->s_op->show_options(m, mnt);
 	seq_putc(m, '\n');
+out:
 	return err;
 }
 
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 3b45537..58c3e6a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -233,7 +233,7 @@
 	 */
 	if (task->parent == current && (task->ptrace & PT_PTRACED) &&
 	    task_is_stopped_or_traced(task) &&
-	    ptrace_may_attach(task))
+	    ptrace_may_access(task, PTRACE_MODE_ATTACH))
 		return 0;
 
 	/*
@@ -251,7 +251,8 @@
 	task_lock(task);
 	if (task->mm != mm)
 		goto out;
-	if (task->mm != current->mm && __ptrace_may_attach(task) < 0)
+	if (task->mm != current->mm &&
+	    __ptrace_may_access(task, PTRACE_MODE_READ) < 0)
 		goto out;
 	task_unlock(task);
 	return mm;
@@ -518,7 +519,7 @@
 	 */
 	task = get_proc_task(inode);
 	if (task) {
-		allowed = ptrace_may_attach(task);
+		allowed = ptrace_may_access(task, PTRACE_MODE_READ);
 		put_task_struct(task);
 	}
 	return allowed;
@@ -904,7 +905,7 @@
 	if (!task)
 		goto out_no_task;
 
-	if (!ptrace_may_attach(task))
+	if (!ptrace_may_access(task, PTRACE_MODE_READ))
 		goto out;
 
 	ret = -ENOMEM;
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index c492449..164bd9f 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -210,7 +210,7 @@
 	dev_t dev = 0;
 	int len;
 
-	if (maps_protect && !ptrace_may_attach(task))
+	if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
 		return -EACCES;
 
 	if (file) {
@@ -646,7 +646,7 @@
 		goto out;
 
 	ret = -EACCES;
-	if (!ptrace_may_attach(task))
+	if (!ptrace_may_access(task, PTRACE_MODE_READ))
 		goto out_task;
 
 	ret = -EINVAL;
@@ -747,7 +747,7 @@
 	struct proc_maps_private *priv = m->private;
 	struct task_struct *task = priv->task;
 
-	if (maps_protect && !ptrace_may_attach(task))
+	if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
 		return -EACCES;
 
 	return show_numa_map(m, v);
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 4b4f9cc..5d84e71 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -113,7 +113,7 @@
 	struct proc_maps_private *priv = m->private;
 	struct task_struct *task = priv->task;
 
-	if (maps_protect && !ptrace_may_attach(task))
+	if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
 		return -EACCES;
 
 	return nommu_vma_show(m, vml->vma);
diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c
index 9590b90..78f613c 100644
--- a/fs/ramfs/file-mmu.c
+++ b/fs/ramfs/file-mmu.c
@@ -45,6 +45,7 @@
 	.mmap		= generic_file_mmap,
 	.fsync		= simple_sync_file,
 	.splice_read	= generic_file_splice_read,
+	.splice_write	= generic_file_splice_write,
 	.llseek		= generic_file_llseek,
 };
 
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
index 0989bc2..52312ec 100644
--- a/fs/ramfs/file-nommu.c
+++ b/fs/ramfs/file-nommu.c
@@ -43,6 +43,7 @@
 	.aio_write		= generic_file_aio_write,
 	.fsync			= simple_sync_file,
 	.splice_read		= generic_file_splice_read,
+	.splice_write		= generic_file_splice_write,
 	.llseek			= generic_file_llseek,
 };
 
diff --git a/fs/splice.c b/fs/splice.c
index aa5f6f6..3994421 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -379,13 +379,22 @@
 				lock_page(page);
 
 			/*
-			 * page was truncated, stop here. if this isn't the
-			 * first page, we'll just complete what we already
-			 * added
+			 * Page was truncated, or invalidated by the
+			 * filesystem.  Redo the find/create, but this time the
+			 * page is kept locked, so there's no chance of another
+			 * race with truncate/invalidate.
 			 */
 			if (!page->mapping) {
 				unlock_page(page);
-				break;
+				page = find_or_create_page(mapping, index,
+						mapping_gfp_mask(mapping));
+
+				if (!page) {
+					error = -ENOMEM;
+					break;
+				}
+				page_cache_release(pages[page_nr]);
+				pages[page_nr] = page;
 			}
 			/*
 			 * page was already under io and is now done, great
diff --git a/include/Kbuild b/include/Kbuild
index b522887..bdca155 100644
--- a/include/Kbuild
+++ b/include/Kbuild
@@ -4,5 +4,6 @@
 header-y += mtd/
 header-y += rdma/
 header-y += video/
+header-y += drm/
 
 header-y += asm-$(ARCH)/
diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h
index a4e2d28..b4cddfa 100644
--- a/include/asm-avr32/arch-at32ap/board.h
+++ b/include/asm-avr32/arch-at32ap/board.h
@@ -8,6 +8,12 @@
 
 #define GPIO_PIN_NONE	(-1)
 
+/*
+ * Clock rates for various on-board oscillators. The number of entries
+ * in this array is chip-dependent.
+ */
+extern unsigned long at32_board_osc_rates[];
+  
 /* Add basic devices: system manager, interrupt controller, portmuxes, etc. */
 void at32_add_system_devices(void);
 
@@ -36,7 +42,8 @@
 struct atmel_lcdfb_info;
 struct platform_device *
 at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
-		     unsigned long fbmem_start, unsigned long fbmem_len);
+		     unsigned long fbmem_start, unsigned long fbmem_len,
+		     unsigned int pin_config);
 
 struct usba_platform_data;
 struct platform_device *
@@ -73,6 +80,7 @@
 struct platform_device *at32_add_device_mci(unsigned int id);
 struct platform_device *at32_add_device_ac97c(unsigned int id);
 struct platform_device *at32_add_device_abdac(unsigned int id);
+struct platform_device *at32_add_device_psif(unsigned int id);
 
 struct cf_platform_data {
 	int	detect_pin;
diff --git a/include/asm-avr32/arch-at32ap/init.h b/include/asm-avr32/arch-at32ap/init.h
index 5e75d85..bc40e3d 100644
--- a/include/asm-avr32/arch-at32ap/init.h
+++ b/include/asm-avr32/arch-at32ap/init.h
@@ -13,10 +13,6 @@
 void setup_platform(void);
 void setup_board(void);
 
-/* Called by setup_platform */
-void at32_clock_init(void);
-void at32_portmux_init(void);
-
 void at32_setup_serial_console(unsigned int usart_id);
 
 #endif /* __ASM_AVR32_AT32AP_INIT_H__ */
diff --git a/include/asm-avr32/arch-at32ap/pm.h b/include/asm-avr32/arch-at32ap/pm.h
index 356e430..979b355 100644
--- a/include/asm-avr32/arch-at32ap/pm.h
+++ b/include/asm-avr32/arch-at32ap/pm.h
@@ -19,6 +19,7 @@
 
 #ifndef __ASSEMBLY__
 extern void cpu_enter_idle(void);
+extern void cpu_enter_standby(unsigned long sdramc_base);
 
 extern bool disable_idle_sleep;
 
@@ -43,6 +44,8 @@
 	else
 		cpu_enter_idle();
 }
+
+void intc_set_suspend_handler(unsigned long offset);
 #endif
 
 #endif /* __ASM_AVR32_ARCH_PM_H */
diff --git a/include/asm-avr32/arch-at32ap/sram.h b/include/asm-avr32/arch-at32ap/sram.h
new file mode 100644
index 0000000..4838dae
--- /dev/null
+++ b/include/asm-avr32/arch-at32ap/sram.h
@@ -0,0 +1,30 @@
+/*
+ * Simple SRAM allocator
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * 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 __ASM_AVR32_ARCH_SRAM_H
+#define __ASM_AVR32_ARCH_SRAM_H
+
+#include <linux/genalloc.h>
+
+extern struct gen_pool *sram_pool;
+
+static inline unsigned long sram_alloc(size_t len)
+{
+	if (!sram_pool)
+		return 0UL;
+
+	return gen_pool_alloc(sram_pool, len);
+}
+
+static inline void sram_free(unsigned long addr, size_t len)
+{
+	return gen_pool_free(sram_pool, addr, len);
+}
+
+#endif /* __ASM_AVR32_ARCH_SRAM_H */
diff --git a/include/asm-avr32/mmu_context.h b/include/asm-avr32/mmu_context.h
index c37c391..27ff234 100644
--- a/include/asm-avr32/mmu_context.h
+++ b/include/asm-avr32/mmu_context.h
@@ -13,7 +13,6 @@
 #define __ASM_AVR32_MMU_CONTEXT_H
 
 #include <asm/tlbflush.h>
-#include <asm/pgalloc.h>
 #include <asm/sysreg.h>
 #include <asm-generic/mm_hooks.h>
 
diff --git a/include/asm-avr32/pci.h b/include/asm-avr32/pci.h
index 0f5f134..a32a023 100644
--- a/include/asm-avr32/pci.h
+++ b/include/asm-avr32/pci.h
@@ -5,4 +5,6 @@
 
 #define PCI_DMA_BUS_IS_PHYS	(1)
 
+#include <asm-generic/pci-dma-compat.h>
+
 #endif /* __ASM_AVR32_PCI_H__ */
diff --git a/include/asm-avr32/pgalloc.h b/include/asm-avr32/pgalloc.h
index 51fc1f6..6408213 100644
--- a/include/asm-avr32/pgalloc.h
+++ b/include/asm-avr32/pgalloc.h
@@ -8,65 +8,79 @@
 #ifndef __ASM_AVR32_PGALLOC_H
 #define __ASM_AVR32_PGALLOC_H
 
-#include <asm/processor.h>
-#include <linux/threads.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
+#include <linux/quicklist.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
 
-#define pmd_populate_kernel(mm, pmd, pte) \
-	set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte)))
+#define QUICK_PGD	0	/* Preserve kernel mappings over free */
+#define QUICK_PT	1	/* Zero on free */
 
-static __inline__ void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
+static inline void pmd_populate_kernel(struct mm_struct *mm,
+				       pmd_t *pmd, pte_t *pte)
+{
+	set_pmd(pmd, __pmd((unsigned long)pte));
+}
+
+static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
 				    pgtable_t pte)
 {
-	set_pmd(pmd, __pmd(_PAGE_TABLE + page_to_phys(pte)));
+	set_pmd(pmd, __pmd((unsigned long)page_address(pte)));
 }
 #define pmd_pgtable(pmd) pmd_page(pmd)
 
+static inline void pgd_ctor(void *x)
+{
+	pgd_t *pgd = x;
+
+	memcpy(pgd + USER_PTRS_PER_PGD,
+		swapper_pg_dir + USER_PTRS_PER_PGD,
+		(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+}
+
 /*
  * Allocate and free page tables
  */
-static __inline__ pgd_t *pgd_alloc(struct mm_struct *mm)
+static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
-	return kcalloc(USER_PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL);
+	return quicklist_alloc(QUICK_PGD, GFP_KERNEL | __GFP_REPEAT, pgd_ctor);
 }
 
 static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
 {
-	kfree(pgd);
+	quicklist_free(QUICK_PGD, NULL, pgd);
 }
 
 static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
 					  unsigned long address)
 {
-	pte_t *pte;
-
-	pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT);
-
-	return pte;
+	return quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL);
 }
 
-static inline struct page *pte_alloc_one(struct mm_struct *mm,
+static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
 					 unsigned long address)
 {
-	struct page *pte;
+	struct page *page;
+	void *pg;
 
-	pte = alloc_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO);
-	if (!pte)
+	pg = quicklist_alloc(QUICK_PT, GFP_KERNEL | __GFP_REPEAT, NULL);
+	if (!pg)
 		return NULL;
-	pgtable_page_ctor(pte);
-	return pte;
+
+	page = virt_to_page(pg);
+	pgtable_page_ctor(page);
+
+	return page;
 }
 
 static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 {
-	free_page((unsigned long)pte);
+	quicklist_free(QUICK_PT, NULL, pte);
 }
 
 static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
 {
 	pgtable_page_dtor(pte);
-	__free_page(pte);
+	quicklist_free_page(QUICK_PT, NULL, pte);
 }
 
 #define __pte_free_tlb(tlb,pte)				\
@@ -75,6 +89,10 @@
 	tlb_remove_page((tlb), pte);			\
 } while (0)
 
-#define check_pgt_cache() do { } while(0)
+static inline void check_pgt_cache(void)
+{
+	quicklist_trim(QUICK_PGD, NULL, 25, 16);
+	quicklist_trim(QUICK_PT, NULL, 25, 16);
+}
 
 #endif /* __ASM_AVR32_PGALLOC_H */
diff --git a/include/asm-avr32/pgtable.h b/include/asm-avr32/pgtable.h
index c0e5e29..fecdda1 100644
--- a/include/asm-avr32/pgtable.h
+++ b/include/asm-avr32/pgtable.h
@@ -129,13 +129,6 @@
 
 #define _PAGE_FLAGS_CACHE_MASK	(_PAGE_CACHABLE | _PAGE_BUFFER | _PAGE_WT)
 
-/* TODO: Check for saneness */
-/* User-mode page table flags (to be set in a pgd or pmd entry) */
-#define _PAGE_TABLE		(_PAGE_PRESENT | _PAGE_TYPE_SMALL | _PAGE_RW \
-				 | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
-/* Kernel-mode page table flags */
-#define _KERNPG_TABLE		(_PAGE_PRESENT | _PAGE_TYPE_SMALL | _PAGE_RW \
-				 | _PAGE_ACCESSED | _PAGE_DIRTY)
 /* Flags that may be modified by software */
 #define _PAGE_CHG_MASK		(PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY \
 				 | _PAGE_FLAGS_CACHE_MASK)
@@ -262,10 +255,14 @@
 }
 
 #define pmd_none(x)	(!pmd_val(x))
-#define pmd_present(x)	(pmd_val(x) & _PAGE_PRESENT)
-#define pmd_clear(xp)	do { set_pmd(xp, __pmd(0)); } while (0)
-#define	pmd_bad(x)	((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER))	\
-			 != _KERNPG_TABLE)
+#define pmd_present(x)	(pmd_val(x))
+
+static inline void pmd_clear(pmd_t *pmdp)
+{
+	set_pmd(pmdp, __pmd(0));
+}
+
+#define	pmd_bad(x)	(pmd_val(x) & ~PAGE_MASK)
 
 /*
  * Permanent address of a page. We don't support highmem, so this is
@@ -303,19 +300,16 @@
 
 #define page_pte(page)	page_pte_prot(page, __pgprot(0))
 
-#define pmd_page_vaddr(pmd)					\
-	((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
-
-#define pmd_page(pmd)	(phys_to_page(pmd_val(pmd)))
+#define pmd_page_vaddr(pmd)	pmd_val(pmd)
+#define pmd_page(pmd)		(virt_to_page(pmd_val(pmd)))
 
 /* to find an entry in a page-table-directory. */
-#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
-#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))
-#define pgd_offset_current(address)				\
-	((pgd_t *)__mfsr(SYSREG_PTBR) + pgd_index(address))
+#define pgd_index(address)	(((address) >> PGDIR_SHIFT)	\
+				 & (PTRS_PER_PGD - 1))
+#define pgd_offset(mm, address)	((mm)->pgd + pgd_index(address))
 
 /* to find an entry in a kernel page-table-directory */
-#define pgd_offset_k(address) pgd_offset(&init_mm, address)
+#define pgd_offset_k(address)	pgd_offset(&init_mm, address)
 
 /* Find an entry in the third-level page table.. */
 #define pte_index(address)				\
diff --git a/include/asm-avr32/thread_info.h b/include/asm-avr32/thread_info.h
index 07049f6..df68631 100644
--- a/include/asm-avr32/thread_info.h
+++ b/include/asm-avr32/thread_info.h
@@ -88,6 +88,7 @@
 #define TIF_MEMDIE		6
 #define TIF_RESTORE_SIGMASK	7	/* restore signal mask in do_signal */
 #define TIF_CPU_GOING_TO_SLEEP	8	/* CPU is entering sleep 0 mode */
+#define TIF_FREEZE		29
 #define TIF_DEBUG		30	/* debugging enabled */
 #define TIF_USERSPACE		31      /* true if FS sets userspace */
 
diff --git a/include/asm-avr32/tlbflush.h b/include/asm-avr32/tlbflush.h
index 5bc7c88..bf90a78 100644
--- a/include/asm-avr32/tlbflush.h
+++ b/include/asm-avr32/tlbflush.h
@@ -26,7 +26,6 @@
 extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 			    unsigned long end);
 extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
-extern void __flush_tlb_page(unsigned long asid, unsigned long page);
 
 extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
 
diff --git a/include/asm-mips/mach-au1x00/au1xxx_psc.h b/include/asm-mips/mach-au1x00/au1xxx_psc.h
index dae4eca..892b7f1 100644
--- a/include/asm-mips/mach-au1x00/au1xxx_psc.h
+++ b/include/asm-mips/mach-au1x00/au1xxx_psc.h
@@ -204,6 +204,14 @@
 	u32	psc_i2sudf;
 } psc_i2s_t;
 
+#define PSC_I2SCFG_OFFSET	0x08
+#define PSC_I2SMASK_OFFSET	0x0C
+#define PSC_I2SPCR_OFFSET	0x10
+#define PSC_I2SSTAT_OFFSET	0x14
+#define PSC_I2SEVENT_OFFSET	0x18
+#define PSC_I2SRXTX_OFFSET	0x1C
+#define PSC_I2SUDF_OFFSET	0x20
+
 /* I2S Config Register. */
 #define PSC_I2SCFG_RT_MASK	(3 << 30)
 #define PSC_I2SCFG_RT_FIFO1	(0 << 30)
diff --git a/include/asm-s390/Kbuild b/include/asm-s390/Kbuild
index 13c9805..09f3125 100644
--- a/include/asm-s390/Kbuild
+++ b/include/asm-s390/Kbuild
@@ -8,6 +8,9 @@
 header-y += vtoc.h
 header-y += zcrypt.h
 header-y += kvm.h
+header-y += schid.h
+header-y += chsc.h
 
 unifdef-y += cmb.h
 unifdef-y += debug.h
+unifdef-y += chpid.h
diff --git a/include/asm-s390/airq.h b/include/asm-s390/airq.h
index 41d028cb..1ac80d6 100644
--- a/include/asm-s390/airq.h
+++ b/include/asm-s390/airq.h
@@ -13,7 +13,7 @@
 
 typedef void (*adapter_int_handler_t)(void *, void *);
 
-void *s390_register_adapter_interrupt(adapter_int_handler_t, void *);
-void s390_unregister_adapter_interrupt(void *);
+void *s390_register_adapter_interrupt(adapter_int_handler_t, void *, u8);
+void s390_unregister_adapter_interrupt(void *, u8);
 
 #endif /* _ASM_S390_AIRQ_H */
diff --git a/include/asm-s390/ccwdev.h b/include/asm-s390/ccwdev.h
index 066aa70..ba007d8 100644
--- a/include/asm-s390/ccwdev.h
+++ b/include/asm-s390/ccwdev.h
@@ -12,6 +12,7 @@
 
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
+#include <asm/fcx.h>
 
 /* structs from asm/cio.h */
 struct irb;
@@ -157,6 +158,17 @@
 extern int ccw_device_resume(struct ccw_device *);
 extern int ccw_device_halt(struct ccw_device *, unsigned long);
 extern int ccw_device_clear(struct ccw_device *, unsigned long);
+int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
+			    unsigned long intparm, u8 lpm, u8 key);
+int ccw_device_tm_start_key(struct ccw_device *, struct tcw *,
+			    unsigned long, u8, u8);
+int ccw_device_tm_start_timeout_key(struct ccw_device *, struct tcw *,
+			    unsigned long, u8, u8, int);
+int ccw_device_tm_start(struct ccw_device *, struct tcw *,
+			    unsigned long, u8);
+int ccw_device_tm_start_timeout(struct ccw_device *, struct tcw *,
+			    unsigned long, u8, int);
+int ccw_device_tm_intrg(struct ccw_device *cdev);
 
 extern int ccw_device_set_online(struct ccw_device *cdev);
 extern int ccw_device_set_offline(struct ccw_device *cdev);
diff --git a/include/asm-s390/chpid.h b/include/asm-s390/chpid.h
index b203336..606844d 100644
--- a/include/asm-s390/chpid.h
+++ b/include/asm-s390/chpid.h
@@ -10,7 +10,6 @@
 
 #include <linux/string.h>
 #include <asm/types.h>
-#include <asm/cio.h>
 
 #define __MAX_CHPID 255
 
@@ -41,6 +40,9 @@
 	}
 }
 
+#ifdef __KERNEL__
+#include <asm/cio.h>
+
 static inline int chp_id_is_valid(struct chp_id *chpid)
 {
 	return (chpid->cssid <= __MAX_CSSID);
@@ -49,5 +51,6 @@
 
 #define chp_id_for_each(c) \
 	for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c))
+#endif /* __KERNEL */
 
 #endif /* _ASM_S390_CHPID_H */
diff --git a/include/asm-s390/chsc.h b/include/asm-s390/chsc.h
new file mode 100644
index 0000000..d38d0cf
--- /dev/null
+++ b/include/asm-s390/chsc.h
@@ -0,0 +1,127 @@
+/*
+ * ioctl interface for /dev/chsc
+ *
+ * Copyright 2008 IBM Corp.
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ */
+
+#ifndef _ASM_CHSC_H
+#define _ASM_CHSC_H
+
+#include <asm/chpid.h>
+#include <asm/schid.h>
+
+struct chsc_async_header {
+	__u16 length;
+	__u16 code;
+	__u32 cmd_dependend;
+	__u32 key : 4;
+	__u32 : 28;
+	struct subchannel_id sid;
+} __attribute__ ((packed));
+
+struct chsc_async_area {
+	struct chsc_async_header header;
+	__u8 data[PAGE_SIZE - 16 /* size of chsc_async_header */];
+} __attribute__ ((packed));
+
+
+struct chsc_response_struct {
+	__u16 length;
+	__u16 code;
+	__u32 parms;
+	__u8 data[PAGE_SIZE - 8];
+} __attribute__ ((packed));
+
+struct chsc_chp_cd {
+	struct chp_id chpid;
+	int m;
+	int fmt;
+	struct chsc_response_struct cpcb;
+};
+
+struct chsc_cu_cd {
+	__u16 cun;
+	__u8 cssid;
+	int m;
+	int fmt;
+	struct chsc_response_struct cucb;
+};
+
+struct chsc_sch_cud {
+	struct subchannel_id schid;
+	int fmt;
+	struct chsc_response_struct scub;
+};
+
+struct conf_id {
+	int m;
+	__u8 cssid;
+	__u8 ssid;
+};
+
+struct chsc_conf_info {
+	struct conf_id id;
+	int fmt;
+	struct chsc_response_struct scid;
+};
+
+struct ccl_parm_chpid {
+	int m;
+	struct chp_id chp;
+};
+
+struct ccl_parm_cssids {
+	__u8 f_cssid;
+	__u8 l_cssid;
+};
+
+struct chsc_comp_list {
+	struct {
+		enum {
+			CCL_CU_ON_CHP = 1,
+			CCL_CHP_TYPE_CAP = 2,
+			CCL_CSS_IMG = 4,
+			CCL_CSS_IMG_CONF_CHAR = 5,
+			CCL_IOP_CHP = 6,
+		} ctype;
+		int fmt;
+		struct ccl_parm_chpid chpid;
+		struct ccl_parm_cssids cssids;
+	} req;
+	struct chsc_response_struct sccl;
+};
+
+struct chsc_dcal {
+	struct {
+		enum {
+			DCAL_CSS_IID_PN = 4,
+		} atype;
+		__u32 list_parm[2];
+		int fmt;
+	} req;
+	struct chsc_response_struct sdcal;
+};
+
+struct chsc_cpd_info {
+	struct chp_id chpid;
+	int m;
+	int fmt;
+	int rfmt;
+	int c;
+	struct chsc_response_struct chpdb;
+};
+
+#define CHSC_IOCTL_MAGIC 'c'
+
+#define CHSC_START _IOWR(CHSC_IOCTL_MAGIC, 0x81, struct chsc_async_area)
+#define CHSC_INFO_CHANNEL_PATH _IOWR(CHSC_IOCTL_MAGIC, 0x82, \
+				    struct chsc_chp_cd)
+#define CHSC_INFO_CU _IOWR(CHSC_IOCTL_MAGIC, 0x83, struct chsc_cu_cd)
+#define CHSC_INFO_SCH_CU _IOWR(CHSC_IOCTL_MAGIC, 0x84, struct chsc_sch_cud)
+#define CHSC_INFO_CI _IOWR(CHSC_IOCTL_MAGIC, 0x85, struct chsc_conf_info)
+#define CHSC_INFO_CCL _IOWR(CHSC_IOCTL_MAGIC, 0x86, struct chsc_comp_list)
+#define CHSC_INFO_CPD _IOWR(CHSC_IOCTL_MAGIC, 0x87, struct chsc_cpd_info)
+#define CHSC_INFO_DCAL _IOWR(CHSC_IOCTL_MAGIC, 0x88, struct chsc_dcal)
+
+#endif
diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h
index 0818ecd..6dccb07 100644
--- a/include/asm-s390/cio.h
+++ b/include/asm-s390/cio.h
@@ -16,7 +16,7 @@
 #define __MAX_CSSID 0
 
 /**
- * struct scsw - subchannel status word
+ * struct cmd_scsw - command-mode subchannel status word
  * @key: subchannel key
  * @sctl: suspend control
  * @eswf: esw format
@@ -38,7 +38,7 @@
  * @cstat: subchannel status
  * @count: residual count
  */
-struct scsw {
+struct cmd_scsw {
 	__u32 key  : 4;
 	__u32 sctl : 1;
 	__u32 eswf : 1;
@@ -61,6 +61,114 @@
 	__u32 count : 16;
 } __attribute__ ((packed));
 
+/**
+ * struct tm_scsw - transport-mode subchannel status word
+ * @key: subchannel key
+ * @eswf: esw format
+ * @cc: deferred condition code
+ * @fmt: format
+ * @x: IRB-format control
+ * @q: interrogate-complete
+ * @ectl: extended control
+ * @pno: path not operational
+ * @fctl: function control
+ * @actl: activity control
+ * @stctl: status control
+ * @tcw: TCW address
+ * @dstat: device status
+ * @cstat: subchannel status
+ * @fcxs: FCX status
+ * @schxs: subchannel-extended status
+ */
+struct tm_scsw {
+	u32 key:4;
+	u32 :1;
+	u32 eswf:1;
+	u32 cc:2;
+	u32 fmt:3;
+	u32 x:1;
+	u32 q:1;
+	u32 :1;
+	u32 ectl:1;
+	u32 pno:1;
+	u32 :1;
+	u32 fctl:3;
+	u32 actl:7;
+	u32 stctl:5;
+	u32 tcw;
+	u32 dstat:8;
+	u32 cstat:8;
+	u32 fcxs:8;
+	u32 schxs:8;
+} __attribute__ ((packed));
+
+/**
+ * union scsw - subchannel status word
+ * @cmd: command-mode SCSW
+ * @tm: transport-mode SCSW
+ */
+union scsw {
+	struct cmd_scsw cmd;
+	struct tm_scsw tm;
+} __attribute__ ((packed));
+
+int scsw_is_tm(union scsw *scsw);
+u32 scsw_key(union scsw *scsw);
+u32 scsw_eswf(union scsw *scsw);
+u32 scsw_cc(union scsw *scsw);
+u32 scsw_ectl(union scsw *scsw);
+u32 scsw_pno(union scsw *scsw);
+u32 scsw_fctl(union scsw *scsw);
+u32 scsw_actl(union scsw *scsw);
+u32 scsw_stctl(union scsw *scsw);
+u32 scsw_dstat(union scsw *scsw);
+u32 scsw_cstat(union scsw *scsw);
+int scsw_is_solicited(union scsw *scsw);
+int scsw_is_valid_key(union scsw *scsw);
+int scsw_is_valid_eswf(union scsw *scsw);
+int scsw_is_valid_cc(union scsw *scsw);
+int scsw_is_valid_ectl(union scsw *scsw);
+int scsw_is_valid_pno(union scsw *scsw);
+int scsw_is_valid_fctl(union scsw *scsw);
+int scsw_is_valid_actl(union scsw *scsw);
+int scsw_is_valid_stctl(union scsw *scsw);
+int scsw_is_valid_dstat(union scsw *scsw);
+int scsw_is_valid_cstat(union scsw *scsw);
+int scsw_cmd_is_valid_key(union scsw *scsw);
+int scsw_cmd_is_valid_sctl(union scsw *scsw);
+int scsw_cmd_is_valid_eswf(union scsw *scsw);
+int scsw_cmd_is_valid_cc(union scsw *scsw);
+int scsw_cmd_is_valid_fmt(union scsw *scsw);
+int scsw_cmd_is_valid_pfch(union scsw *scsw);
+int scsw_cmd_is_valid_isic(union scsw *scsw);
+int scsw_cmd_is_valid_alcc(union scsw *scsw);
+int scsw_cmd_is_valid_ssi(union scsw *scsw);
+int scsw_cmd_is_valid_zcc(union scsw *scsw);
+int scsw_cmd_is_valid_ectl(union scsw *scsw);
+int scsw_cmd_is_valid_pno(union scsw *scsw);
+int scsw_cmd_is_valid_fctl(union scsw *scsw);
+int scsw_cmd_is_valid_actl(union scsw *scsw);
+int scsw_cmd_is_valid_stctl(union scsw *scsw);
+int scsw_cmd_is_valid_dstat(union scsw *scsw);
+int scsw_cmd_is_valid_cstat(union scsw *scsw);
+int scsw_cmd_is_solicited(union scsw *scsw);
+int scsw_tm_is_valid_key(union scsw *scsw);
+int scsw_tm_is_valid_eswf(union scsw *scsw);
+int scsw_tm_is_valid_cc(union scsw *scsw);
+int scsw_tm_is_valid_fmt(union scsw *scsw);
+int scsw_tm_is_valid_x(union scsw *scsw);
+int scsw_tm_is_valid_q(union scsw *scsw);
+int scsw_tm_is_valid_ectl(union scsw *scsw);
+int scsw_tm_is_valid_pno(union scsw *scsw);
+int scsw_tm_is_valid_fctl(union scsw *scsw);
+int scsw_tm_is_valid_actl(union scsw *scsw);
+int scsw_tm_is_valid_stctl(union scsw *scsw);
+int scsw_tm_is_valid_dstat(union scsw *scsw);
+int scsw_tm_is_valid_cstat(union scsw *scsw);
+int scsw_tm_is_valid_fcxs(union scsw *scsw);
+int scsw_tm_is_valid_schxs(union scsw *scsw);
+int scsw_tm_is_solicited(union scsw *scsw);
+
 #define SCSW_FCTL_CLEAR_FUNC	 0x1
 #define SCSW_FCTL_HALT_FUNC	 0x2
 #define SCSW_FCTL_START_FUNC	 0x4
@@ -303,7 +411,7 @@
  * if applicable).
  */
 struct irb {
-	struct scsw scsw;
+	union scsw scsw;
 	union {
 		struct esw0 esw0;
 		struct esw1 esw1;
diff --git a/include/asm-s390/elf.h b/include/asm-s390/elf.h
index b3ac262..3cad569 100644
--- a/include/asm-s390/elf.h
+++ b/include/asm-s390/elf.h
@@ -113,6 +113,9 @@
 typedef s390_fp_regs elf_fpregset_t;
 typedef s390_regs elf_gregset_t;
 
+typedef s390_fp_regs compat_elf_fpregset_t;
+typedef s390_compat_regs compat_elf_gregset_t;
+
 #include <linux/sched.h>	/* for task_struct */
 #include <asm/system.h>		/* for save_access_regs */
 #include <asm/mmu_context.h>
@@ -123,6 +126,10 @@
 #define elf_check_arch(x) \
 	(((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
          && (x)->e_ident[EI_CLASS] == ELF_CLASS) 
+#define compat_elf_check_arch(x) \
+	(((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
+	 && (x)->e_ident[EI_CLASS] == ELF_CLASS)
+#define compat_start_thread	start_thread31
 
 /* For SVR4/S390 the function pointer to be registered with `atexit` is
    passed in R14. */
@@ -131,6 +138,7 @@
 		_r->gprs[14] = 0; \
 	} while (0)
 
+#define CORE_DUMP_USE_REGSET
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	4096
 
@@ -140,44 +148,6 @@
    that it will "exec", and that there is sufficient room for the brk.  */
 #define ELF_ET_DYN_BASE		(STACK_TOP / 3 * 2)
 
-/* Wow, the "main" arch needs arch dependent functions too.. :) */
-
-/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
-   now struct_user_regs, they are different) */
-
-static inline int dump_regs(struct pt_regs *ptregs, elf_gregset_t *regs)
-{
-	memcpy(&regs->psw, &ptregs->psw, sizeof(regs->psw)+sizeof(regs->gprs));
-	save_access_regs(regs->acrs);
-	regs->orig_gpr2 = ptregs->orig_gpr2;
-	return 1;
-}
-
-#define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs(regs, &pr_reg);
-
-static inline int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
-{
-	struct pt_regs *ptregs = task_pt_regs(tsk);
-	memcpy(&regs->psw, &ptregs->psw, sizeof(regs->psw)+sizeof(regs->gprs));
-	memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs));
-	regs->orig_gpr2 = ptregs->orig_gpr2;
-	return 1;
-}
-
-#define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs(tsk, regs)
-
-static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
-{
-	if (tsk == current)
-		save_fp_regs(fpregs);
-	else
-		memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t));
-	return 1;
-}
-
-#define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs)
-
-
 /* This yields a mask that user programs can use to figure out what
    instruction set this CPU supports. */
 
@@ -204,7 +174,10 @@
 		set_personality(PER_SVR4);		\
 	else if (current->personality != PER_LINUX32)	\
 		set_personality(PER_LINUX);		\
-	clear_thread_flag(TIF_31BIT);			\
+	if ((ex).e_ident[EI_CLASS] == ELFCLASS32)	\
+		set_thread_flag(TIF_31BIT);		\
+	else						\
+		clear_thread_flag(TIF_31BIT);		\
 } while (0)
 #endif /* __s390x__ */
 
diff --git a/include/asm-s390/etr.h b/include/asm-s390/etr.h
index b498f19..80ef58c 100644
--- a/include/asm-s390/etr.h
+++ b/include/asm-s390/etr.h
@@ -122,7 +122,7 @@
 } __attribute__ ((packed,aligned(8)));
 
 /* ETR interruption parameter */
-struct etr_interruption_parameter {
+struct etr_irq_parm {
 	unsigned int _pad0	: 8;
 	unsigned int pc0	: 1;	/* port 0 state change */
 	unsigned int pc1	: 1;	/* port 1 state change */
@@ -213,7 +213,46 @@
 #define ETR_PTFF_SGS	0x43	/* set gross steering rate */
 
 /* Functions needed by the machine check handler */
-extern void etr_switch_to_local(void);
-extern void etr_sync_check(void);
+void etr_switch_to_local(void);
+void etr_sync_check(void);
+
+/* STP interruption parameter */
+struct stp_irq_parm {
+	unsigned int _pad0	: 14;
+	unsigned int tsc	: 1;	/* Timing status change */
+	unsigned int lac	: 1;	/* Link availability change */
+	unsigned int tcpc	: 1;	/* Time control parameter change */
+	unsigned int _pad2	: 15;
+} __attribute__ ((packed));
+
+#define STP_OP_SYNC	1
+#define STP_OP_CTRL	3
+
+struct stp_sstpi {
+	unsigned int rsvd0;
+	unsigned int rsvd1 : 8;
+	unsigned int stratum : 8;
+	unsigned int vbits : 16;
+	unsigned int leaps : 16;
+	unsigned int tmd : 4;
+	unsigned int ctn : 4;
+	unsigned int rsvd2 : 3;
+	unsigned int c : 1;
+	unsigned int tst : 4;
+	unsigned int tzo : 16;
+	unsigned int dsto : 16;
+	unsigned int ctrl : 16;
+	unsigned int rsvd3 : 16;
+	unsigned int tto;
+	unsigned int rsvd4;
+	unsigned int ctnid[3];
+	unsigned int rsvd5;
+	unsigned int todoff[4];
+	unsigned int rsvd6[48];
+} __attribute__ ((packed));
+
+/* Functions needed by the machine check handler */
+void stp_sync_check(void);
+void stp_island_check(void);
 
 #endif /* __S390_ETR_H */
diff --git a/include/asm-s390/fcx.h b/include/asm-s390/fcx.h
new file mode 100644
index 0000000..8be1f3a
--- /dev/null
+++ b/include/asm-s390/fcx.h
@@ -0,0 +1,311 @@
+/*
+ *  Functions for assembling fcx enabled I/O control blocks.
+ *
+ *    Copyright IBM Corp. 2008
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_FCX_H
+#define _ASM_S390_FCX_H _ASM_S390_FCX_H
+
+#include <linux/types.h>
+
+#define TCW_FORMAT_DEFAULT		0
+#define TCW_TIDAW_FORMAT_DEFAULT	0
+#define TCW_FLAGS_INPUT_TIDA		1 << (23 - 5)
+#define TCW_FLAGS_TCCB_TIDA		1 << (23 - 6)
+#define TCW_FLAGS_OUTPUT_TIDA		1 << (23 - 7)
+#define TCW_FLAGS_TIDAW_FORMAT(x)	((x) & 3) << (23 - 9)
+#define TCW_FLAGS_GET_TIDAW_FORMAT(x)	(((x) >> (23 - 9)) & 3)
+
+/**
+ * struct tcw - Transport Control Word (TCW)
+ * @format: TCW format
+ * @flags: TCW flags
+ * @tccbl: Transport-Command-Control-Block Length
+ * @r: Read Operations
+ * @w: Write Operations
+ * @output: Output-Data Address
+ * @input: Input-Data Address
+ * @tsb: Transport-Status-Block Address
+ * @tccb: Transport-Command-Control-Block Address
+ * @output_count: Output Count
+ * @input_count: Input Count
+ * @intrg: Interrogate TCW Address
+ */
+struct tcw {
+	u32 format:2;
+	u32 :6;
+	u32 flags:24;
+	u32 :8;
+	u32 tccbl:6;
+	u32 r:1;
+	u32 w:1;
+	u32 :16;
+	u64 output;
+	u64 input;
+	u64 tsb;
+	u64 tccb;
+	u32 output_count;
+	u32 input_count;
+	u32 :32;
+	u32 :32;
+	u32 :32;
+	u32 intrg;
+} __attribute__ ((packed, aligned(64)));
+
+#define TIDAW_FLAGS_LAST		1 << (7 - 0)
+#define TIDAW_FLAGS_SKIP		1 << (7 - 1)
+#define TIDAW_FLAGS_DATA_INT		1 << (7 - 2)
+#define TIDAW_FLAGS_TTIC		1 << (7 - 3)
+#define TIDAW_FLAGS_INSERT_CBC		1 << (7 - 4)
+
+/**
+ * struct tidaw - Transport-Indirect-Addressing Word (TIDAW)
+ * @flags: TIDAW flags. Can be an arithmetic OR of the following constants:
+ * %TIDAW_FLAGS_LAST, %TIDAW_FLAGS_SKIP, %TIDAW_FLAGS_DATA_INT,
+ * %TIDAW_FLAGS_TTIC, %TIDAW_FLAGS_INSERT_CBC
+ * @count: Count
+ * @addr: Address
+ */
+struct tidaw {
+	u32 flags:8;
+	u32 :24;
+	u32 count;
+	u64 addr;
+} __attribute__ ((packed, aligned(16)));
+
+/**
+ * struct tsa_iostat - I/O-Status Transport-Status Area (IO-Stat TSA)
+ * @dev_time: Device Time
+ * @def_time: Defer Time
+ * @queue_time: Queue Time
+ * @dev_busy_time: Device-Busy Time
+ * @dev_act_time: Device-Active-Only Time
+ * @sense: Sense Data (if present)
+ */
+struct tsa_iostat {
+	u32 dev_time;
+	u32 def_time;
+	u32 queue_time;
+	u32 dev_busy_time;
+	u32 dev_act_time;
+	u8 sense[32];
+} __attribute__ ((packed));
+
+/**
+ * struct tsa_ddpcs - Device-Detected-Program-Check Transport-Status Area (DDPC TSA)
+ * @rc: Reason Code
+ * @rcq: Reason Code Qualifier
+ * @sense: Sense Data (if present)
+ */
+struct tsa_ddpc {
+	u32 :24;
+	u32 rc:8;
+	u8 rcq[16];
+	u8 sense[32];
+} __attribute__ ((packed));
+
+#define TSA_INTRG_FLAGS_CU_STATE_VALID		1 << (7 - 0)
+#define TSA_INTRG_FLAGS_DEV_STATE_VALID		1 << (7 - 1)
+#define TSA_INTRG_FLAGS_OP_STATE_VALID		1 << (7 - 2)
+
+/**
+ * struct tsa_intrg - Interrogate Transport-Status Area (Intrg. TSA)
+ * @format: Format
+ * @flags: Flags. Can be an arithmetic OR of the following constants:
+ * %TSA_INTRG_FLAGS_CU_STATE_VALID, %TSA_INTRG_FLAGS_DEV_STATE_VALID,
+ * %TSA_INTRG_FLAGS_OP_STATE_VALID
+ * @cu_state: Controle-Unit State
+ * @dev_state: Device State
+ * @op_state: Operation State
+ * @sd_info: State-Dependent Information
+ * @dl_id: Device-Level Identifier
+ * @dd_data: Device-Dependent Data
+ */
+struct tsa_intrg {
+	u32 format:8;
+	u32 flags:8;
+	u32 cu_state:8;
+	u32 dev_state:8;
+	u32 op_state:8;
+	u32 :24;
+	u8 sd_info[12];
+	u32 dl_id;
+	u8 dd_data[28];
+} __attribute__ ((packed));
+
+#define TSB_FORMAT_NONE		0
+#define TSB_FORMAT_IOSTAT	1
+#define TSB_FORMAT_DDPC		2
+#define TSB_FORMAT_INTRG	3
+
+#define TSB_FLAGS_DCW_OFFSET_VALID	1 << (7 - 0)
+#define TSB_FLAGS_COUNT_VALID		1 << (7 - 1)
+#define TSB_FLAGS_CACHE_MISS		1 << (7 - 2)
+#define TSB_FLAGS_TIME_VALID		1 << (7 - 3)
+#define TSB_FLAGS_FORMAT(x)		((x) & 7)
+#define TSB_FORMAT(t)			((t)->flags & 7)
+
+/**
+ * struct tsb - Transport-Status Block (TSB)
+ * @length: Length
+ * @flags: Flags. Can be an arithmetic OR of the following constants:
+ * %TSB_FLAGS_DCW_OFFSET_VALID, %TSB_FLAGS_COUNT_VALID, %TSB_FLAGS_CACHE_MISS,
+ * %TSB_FLAGS_TIME_VALID
+ * @dcw_offset: DCW Offset
+ * @count: Count
+ * @tsa: Transport-Status-Area
+ */
+struct tsb {
+	u32 length:8;
+	u32 flags:8;
+	u32 dcw_offset:16;
+	u32 count;
+	u32 :32;
+	union {
+		struct tsa_iostat iostat;
+		struct tsa_ddpc ddpc;
+		struct tsa_intrg intrg;
+	} __attribute__ ((packed)) tsa;
+} __attribute__ ((packed, aligned(8)));
+
+#define DCW_INTRG_FORMAT_DEFAULT	0
+
+#define DCW_INTRG_RC_UNSPECIFIED	0
+#define DCW_INTRG_RC_TIMEOUT		1
+
+#define DCW_INTRG_RCQ_UNSPECIFIED	0
+#define DCW_INTRG_RCQ_PRIMARY		1
+#define DCW_INTRG_RCQ_SECONDARY		2
+
+#define DCW_INTRG_FLAGS_MPM		1 < (7 - 0)
+#define DCW_INTRG_FLAGS_PPR		1 < (7 - 1)
+#define DCW_INTRG_FLAGS_CRIT		1 < (7 - 2)
+
+/**
+ * struct dcw_intrg_data - Interrogate DCW data
+ * @format: Format. Should be %DCW_INTRG_FORMAT_DEFAULT
+ * @rc: Reason Code. Can be one of %DCW_INTRG_RC_UNSPECIFIED,
+ * %DCW_INTRG_RC_TIMEOUT
+ * @rcq: Reason Code Qualifier: Can be one of %DCW_INTRG_RCQ_UNSPECIFIED,
+ * %DCW_INTRG_RCQ_PRIMARY, %DCW_INTRG_RCQ_SECONDARY
+ * @lpm: Logical-Path Mask
+ * @pam: Path-Available Mask
+ * @pim: Path-Installed Mask
+ * @timeout: Timeout
+ * @flags: Flags. Can be an arithmetic OR of %DCW_INTRG_FLAGS_MPM,
+ * %DCW_INTRG_FLAGS_PPR, %DCW_INTRG_FLAGS_CRIT
+ * @time: Time
+ * @prog_id: Program Identifier
+ * @prog_data: Program-Dependent Data
+ */
+struct dcw_intrg_data {
+	u32 format:8;
+	u32 rc:8;
+	u32 rcq:8;
+	u32 lpm:8;
+	u32 pam:8;
+	u32 pim:8;
+	u32 timeout:16;
+	u32 flags:8;
+	u32 :24;
+	u32 :32;
+	u64 time;
+	u64 prog_id;
+	u8  prog_data[0];
+} __attribute__ ((packed));
+
+#define DCW_FLAGS_CC		1 << (7 - 1)
+
+#define DCW_CMD_WRITE		0x01
+#define DCW_CMD_READ		0x02
+#define DCW_CMD_CONTROL		0x03
+#define DCW_CMD_SENSE		0x04
+#define DCW_CMD_SENSE_ID	0xe4
+#define DCW_CMD_INTRG		0x40
+
+/**
+ * struct dcw - Device-Command Word (DCW)
+ * @cmd: Command Code. Can be one of %DCW_CMD_WRITE, %DCW_CMD_READ,
+ * %DCW_CMD_CONTROL, %DCW_CMD_SENSE, %DCW_CMD_SENSE_ID, %DCW_CMD_INTRG
+ * @flags: Flags. Can be an arithmetic OR of %DCW_FLAGS_CC
+ * @cd_count: Control-Data Count
+ * @count: Count
+ * @cd: Control Data
+ */
+struct dcw {
+	u32 cmd:8;
+	u32 flags:8;
+	u32 :8;
+	u32 cd_count:8;
+	u32 count;
+	u8 cd[0];
+} __attribute__ ((packed));
+
+#define TCCB_FORMAT_DEFAULT	0x7f
+#define TCCB_MAX_DCW		30
+#define TCCB_MAX_SIZE		(sizeof(struct tccb_tcah) + \
+				 TCCB_MAX_DCW * sizeof(struct dcw) + \
+				 sizeof(struct tccb_tcat))
+#define TCCB_SAC_DEFAULT	0xf901
+#define TCCB_SAC_INTRG		0xf902
+
+/**
+ * struct tccb_tcah - Transport-Command-Area Header (TCAH)
+ * @format: Format. Should be %TCCB_FORMAT_DEFAULT
+ * @tcal: Transport-Command-Area Length
+ * @sac: Service-Action Code. Can be one of %TCCB_SAC_DEFAULT, %TCCB_SAC_INTRG
+ * @prio: Priority
+ */
+struct tccb_tcah {
+	u32 format:8;
+	u32 :24;
+	u32 :24;
+	u32 tcal:8;
+	u32 sac:16;
+	u32 :8;
+	u32 prio:8;
+	u32 :32;
+} __attribute__ ((packed));
+
+/**
+ * struct tccb_tcat - Transport-Command-Area Trailer (TCAT)
+ * @count: Transport Count
+ */
+struct tccb_tcat {
+	u32 :32;
+	u32 count;
+} __attribute__ ((packed));
+
+/**
+ * struct tccb - (partial) Transport-Command-Control Block (TCCB)
+ * @tcah: TCAH
+ * @tca: Transport-Command Area
+ */
+struct tccb {
+	struct tccb_tcah tcah;
+	u8 tca[0];
+} __attribute__ ((packed, aligned(8)));
+
+struct tcw *tcw_get_intrg(struct tcw *tcw);
+void *tcw_get_data(struct tcw *tcw);
+struct tccb *tcw_get_tccb(struct tcw *tcw);
+struct tsb *tcw_get_tsb(struct tcw *tcw);
+
+void tcw_init(struct tcw *tcw, int r, int w);
+void tcw_finalize(struct tcw *tcw, int num_tidaws);
+
+void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw);
+void tcw_set_data(struct tcw *tcw, void *data, int use_tidal);
+void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb);
+void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb);
+
+void tccb_init(struct tccb *tccb, size_t tccb_size, u32 sac);
+void tsb_init(struct tsb *tsb);
+struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags,
+			 void *cd, u8 cd_count, u32 count);
+struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags,
+			    void *addr, u32 count);
+
+#endif /* _ASM_S390_FCX_H */
diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h
index c1b2e50..eaca6df 100644
--- a/include/asm-s390/ipl.h
+++ b/include/asm-s390/ipl.h
@@ -56,15 +56,19 @@
 	u8  scp_data[];
 } __attribute__((packed));
 
+#define DIAG308_VMPARM_SIZE	64
+
 struct ipl_block_ccw {
-	u8  load_param[8];
+	u8  load_parm[8];
 	u8  reserved1[84];
 	u8  reserved2[2];
 	u16 devno;
 	u8  vm_flags;
 	u8  reserved3[3];
 	u32 vm_parm_len;
-	u8  reserved4[80];
+	u8  nss_name[8];
+	u8  vm_parm[DIAG308_VMPARM_SIZE];
+	u8  reserved4[8];
 } __attribute__((packed));
 
 struct ipl_parameter_block {
@@ -73,7 +77,7 @@
 		struct ipl_block_fcp fcp;
 		struct ipl_block_ccw ccw;
 	} ipl_info;
-} __attribute__((packed));
+} __attribute__((packed,aligned(4096)));
 
 /*
  * IPL validity flags
@@ -86,6 +90,8 @@
 extern void do_halt(void);
 extern void do_poff(void);
 extern void ipl_save_parameters(void);
+extern void ipl_update_parameters(void);
+extern void get_ipl_vmparm(char *);
 
 enum {
 	IPL_DEVNO_VALID		= 1,
@@ -147,6 +153,11 @@
 	DIAG308_FLAGS_LP_VALID	= 0x80,
 };
 
+enum diag308_vm_flags {
+	DIAG308_VM_FLAGS_NSS_VALID	= 0x80,
+	DIAG308_VM_FLAGS_VP_VALID	= 0x40,
+};
+
 enum diag308_rc {
 	DIAG308_RC_OK	= 1,
 };
diff --git a/include/asm-s390/isc.h b/include/asm-s390/isc.h
new file mode 100644
index 0000000..34bb891
--- /dev/null
+++ b/include/asm-s390/isc.h
@@ -0,0 +1,25 @@
+#ifndef _ASM_S390_ISC_H
+#define _ASM_S390_ISC_H
+
+#include <linux/types.h>
+
+/*
+ * I/O interruption subclasses used by drivers.
+ * Please add all used iscs here so that it is possible to distribute
+ * isc usage between drivers.
+ * Reminder: 0 is highest priority, 7 lowest.
+ */
+#define MAX_ISC 7
+
+/* Regular I/O interrupts. */
+#define IO_SCH_ISC 3			/* regular I/O subchannels */
+#define CONSOLE_ISC 1			/* console I/O subchannel */
+#define CHSC_SCH_ISC 7			/* CHSC subchannels */
+/* Adapter interrupts. */
+#define QDIO_AIRQ_ISC IO_SCH_ISC	/* I/O subchannel in qdio mode */
+
+/* Functions for registration of I/O interruption subclasses */
+void isc_register(unsigned int isc);
+void isc_unregister(unsigned int isc);
+
+#endif /* _ASM_S390_ISC_H */
diff --git a/include/asm-s390/itcw.h b/include/asm-s390/itcw.h
new file mode 100644
index 0000000..a9bc5c3
--- /dev/null
+++ b/include/asm-s390/itcw.h
@@ -0,0 +1,30 @@
+/*
+ *  Functions for incremental construction of fcx enabled I/O control blocks.
+ *
+ *    Copyright IBM Corp. 2008
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_ITCW_H
+#define _ASM_S390_ITCW_H _ASM_S390_ITCW_H
+
+#include <linux/types.h>
+#include <asm/fcx.h>
+
+#define ITCW_OP_READ	0
+#define ITCW_OP_WRITE	1
+
+struct itcw;
+
+struct tcw *itcw_get_tcw(struct itcw *itcw);
+size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws);
+struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
+		       int max_tidaws, int intrg_max_tidaws);
+struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd,
+			 u8 cd_count, u32 count);
+struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr,
+			     u32 count);
+void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal);
+void itcw_finalize(struct itcw *itcw);
+
+#endif /* _ASM_S390_ITCW_H */
diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h
index bd0ea19..0bdb704 100644
--- a/include/asm-s390/pgtable.h
+++ b/include/asm-s390/pgtable.h
@@ -29,6 +29,7 @@
  * the S390 page table tree.
  */
 #ifndef __ASSEMBLY__
+#include <linux/sched.h>
 #include <linux/mm_types.h>
 #include <asm/bitops.h>
 #include <asm/bug.h>
diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h
index a00f79d..4af80af 100644
--- a/include/asm-s390/processor.h
+++ b/include/asm-s390/processor.h
@@ -143,11 +143,19 @@
 /*
  * Do necessary setup to start up a new thread.
  */
-#define start_thread(regs, new_psw, new_stackp) do {            \
+#define start_thread(regs, new_psw, new_stackp) do {		\
 	set_fs(USER_DS);					\
 	regs->psw.mask	= psw_user_bits;			\
-        regs->psw.addr  = new_psw | PSW_ADDR_AMODE;             \
-        regs->gprs[15]  = new_stackp ;                          \
+	regs->psw.addr	= new_psw | PSW_ADDR_AMODE;		\
+	regs->gprs[15]	= new_stackp;				\
+} while (0)
+
+#define start_thread31(regs, new_psw, new_stackp) do {		\
+	set_fs(USER_DS);					\
+	regs->psw.mask	= psw_user32_bits;			\
+	regs->psw.addr	= new_psw | PSW_ADDR_AMODE;		\
+	regs->gprs[15]	= new_stackp;				\
+	crst_table_downgrade(current->mm, 1UL << 31);		\
 } while (0)
 
 /* Forward declaration, a strange C thing */
@@ -328,16 +336,6 @@
 extern void (*s390_base_pgm_handler_fn)(void);
 extern void (*s390_base_ext_handler_fn)(void);
 
-/*
- * CPU idle notifier chain.
- */
-#define S390_CPU_IDLE		0
-#define S390_CPU_NOT_IDLE	1
-
-struct notifier_block;
-int register_idle_notifier(struct notifier_block *nb);
-int unregister_idle_notifier(struct notifier_block *nb);
-
 #define ARCH_LOW_ADDRESS_LIMIT	0x7fffffffUL
 
 #endif
diff --git a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h
index d7d4e2e..af2c9ac 100644
--- a/include/asm-s390/ptrace.h
+++ b/include/asm-s390/ptrace.h
@@ -215,6 +215,12 @@
         unsigned long addr;
 } __attribute__ ((aligned(8))) psw_t;
 
+typedef struct
+{
+	__u32	mask;
+	__u32	addr;
+} __attribute__ ((aligned(8))) psw_compat_t;
+
 #ifndef __s390x__
 
 #define PSW_MASK_PER		0x40000000UL
@@ -292,6 +298,15 @@
 	unsigned long orig_gpr2;
 } s390_regs;
 
+typedef struct
+{
+	psw_compat_t	psw;
+	__u32		gprs[NUM_GPRS];
+	__u32		acrs[NUM_ACRS];
+	__u32		orig_gpr2;
+} s390_compat_regs;
+
+
 #ifdef __KERNEL__
 #include <asm/setup.h>
 #include <asm/page.h>
diff --git a/drivers/s390/cio/schid.h b/include/asm-s390/schid.h
similarity index 65%
rename from drivers/s390/cio/schid.h
rename to include/asm-s390/schid.h
index 54328fe..5017ffa 100644
--- a/drivers/s390/cio/schid.h
+++ b/include/asm-s390/schid.h
@@ -1,12 +1,14 @@
-#ifndef S390_SCHID_H
-#define S390_SCHID_H
+#ifndef ASM_SCHID_H
+#define ASM_SCHID_H
 
 struct subchannel_id {
-	__u32 reserved:13;
-	__u32 ssid:2;
-	__u32 one:1;
-	__u32 sch_no:16;
-} __attribute__ ((packed,aligned(4)));
+	__u32 cssid : 8;
+	__u32 : 4;
+	__u32 m : 1;
+	__u32 ssid : 2;
+	__u32 one : 1;
+	__u32 sch_no : 16;
+} __attribute__ ((packed, aligned(4)));
 
 
 /* Helper function for sane state of pre-allocated subchannel_id. */
@@ -23,4 +25,4 @@
 	return !memcmp(schid1, schid2, sizeof(struct subchannel_id));
 }
 
-#endif /* S390_SCHID_H */
+#endif /* ASM_SCHID_H */
diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h
index b5f2843..fed7bee 100644
--- a/include/asm-s390/sclp.h
+++ b/include/asm-s390/sclp.h
@@ -45,9 +45,9 @@
 int sclp_get_cpu_info(struct sclp_cpu_info *info);
 int sclp_cpu_configure(u8 cpu);
 int sclp_cpu_deconfigure(u8 cpu);
-void sclp_read_info_early(void);
 void sclp_facilities_detect(void);
-unsigned long long sclp_memory_detect(void);
+unsigned long long sclp_get_rnmax(void);
+unsigned long long sclp_get_rzm(void);
 int sclp_sdias_blk_count(void);
 int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
 int sclp_chp_configure(struct chp_id chpid);
diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h
index ba69674..f09ee3f 100644
--- a/include/asm-s390/setup.h
+++ b/include/asm-s390/setup.h
@@ -8,14 +8,16 @@
 #ifndef _ASM_S390_SETUP_H
 #define _ASM_S390_SETUP_H
 
-#define COMMAND_LINE_SIZE 	896
+#define COMMAND_LINE_SIZE	1024
+
+#define ARCH_COMMAND_LINE_SIZE	896
 
 #ifdef __KERNEL__
 
 #include <asm/types.h>
 
 #define PARMAREA		0x10400
-#define MEMORY_CHUNKS		16	/* max 0x7fff */
+#define MEMORY_CHUNKS		256
 
 #ifndef __ASSEMBLY__
 
@@ -36,12 +38,14 @@
 struct mem_chunk {
 	unsigned long addr;
 	unsigned long size;
-	unsigned long type;
+	int type;
 };
 
 extern struct mem_chunk memory_chunk[];
 extern unsigned long real_memory_size;
 
+void detect_memory_layout(struct mem_chunk chunk[]);
+
 #ifdef CONFIG_S390_SWITCH_AMODE
 extern unsigned int switch_amode;
 #else
@@ -61,7 +65,6 @@
 
 #define MACHINE_FLAG_VM		(1UL << 0)
 #define MACHINE_FLAG_IEEE	(1UL << 1)
-#define MACHINE_FLAG_P390	(1UL << 2)
 #define MACHINE_FLAG_CSP	(1UL << 3)
 #define MACHINE_FLAG_MVPG	(1UL << 4)
 #define MACHINE_FLAG_DIAG44	(1UL << 5)
@@ -97,7 +100,6 @@
 #define MACHINE_HAS_PFMF	(machine_flags & MACHINE_FLAG_PFMF)
 #endif /* __s390x__ */
 
-#define MACHINE_HAS_SCLP	(!MACHINE_IS_P390)
 #define ZFCPDUMP_HSA_SIZE	(32UL<<20)
 
 /*
diff --git a/include/asm-s390/sparsemem.h b/include/asm-s390/sparsemem.h
index 06dfdab..545d219 100644
--- a/include/asm-s390/sparsemem.h
+++ b/include/asm-s390/sparsemem.h
@@ -1,15 +1,15 @@
 #ifndef _ASM_S390_SPARSEMEM_H
 #define _ASM_S390_SPARSEMEM_H
 
-#define SECTION_SIZE_BITS	25
-
 #ifdef CONFIG_64BIT
 
+#define SECTION_SIZE_BITS	28
 #define MAX_PHYSADDR_BITS	42
 #define MAX_PHYSMEM_BITS	42
 
 #else
 
+#define SECTION_SIZE_BITS	25
 #define MAX_PHYSADDR_BITS	31
 #define MAX_PHYSMEM_BITS	31
 
diff --git a/include/asm-s390/timer.h b/include/asm-s390/timer.h
index adb3486..d98d79e 100644
--- a/include/asm-s390/timer.h
+++ b/include/asm-s390/timer.h
@@ -48,6 +48,18 @@
 extern void init_cpu_vtimer(void);
 extern void vtime_init(void);
 
+#ifdef CONFIG_VIRT_TIMER
+
+extern void vtime_start_cpu_timer(void);
+extern void vtime_stop_cpu_timer(void);
+
+#else
+
+static inline void vtime_start_cpu_timer(void) { }
+static inline void vtime_stop_cpu_timer(void) { }
+
+#endif /* CONFIG_VIRT_TIMER */
+
 #endif /* __KERNEL__ */
 
 #endif /* _ASM_S390_TIMER_H */
diff --git a/include/asm-s390/zcrypt.h b/include/asm-s390/zcrypt.h
index f228f1b..00d3bbd 100644
--- a/include/asm-s390/zcrypt.h
+++ b/include/asm-s390/zcrypt.h
@@ -29,7 +29,7 @@
 
 #define ZCRYPT_VERSION 2
 #define ZCRYPT_RELEASE 1
-#define ZCRYPT_VARIANT 0
+#define ZCRYPT_VARIANT 1
 
 #include <linux/ioctl.h>
 #include <linux/compiler.h>
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
new file mode 100644
index 0000000..d12498e
--- /dev/null
+++ b/include/crypto/hash.h
@@ -0,0 +1,154 @@
+/*
+ * Hash: Hash algorithms under the crypto API
+ * 
+ * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 _CRYPTO_HASH_H
+#define _CRYPTO_HASH_H
+
+#include <linux/crypto.h>
+
+struct crypto_ahash {
+	struct crypto_tfm base;
+};
+
+static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm *tfm)
+{
+	return (struct crypto_ahash *)tfm;
+}
+
+static inline struct crypto_ahash *crypto_alloc_ahash(const char *alg_name,
+						      u32 type, u32 mask)
+{
+	type &= ~CRYPTO_ALG_TYPE_MASK;
+	mask &= ~CRYPTO_ALG_TYPE_MASK;
+	type |= CRYPTO_ALG_TYPE_AHASH;
+	mask |= CRYPTO_ALG_TYPE_AHASH_MASK;
+
+	return __crypto_ahash_cast(crypto_alloc_base(alg_name, type, mask));
+}
+
+static inline struct crypto_tfm *crypto_ahash_tfm(struct crypto_ahash *tfm)
+{
+	return &tfm->base;
+}
+
+static inline void crypto_free_ahash(struct crypto_ahash *tfm)
+{
+	crypto_free_tfm(crypto_ahash_tfm(tfm));
+}
+
+static inline unsigned int crypto_ahash_alignmask(
+	struct crypto_ahash *tfm)
+{
+	return crypto_tfm_alg_alignmask(crypto_ahash_tfm(tfm));
+}
+
+static inline struct ahash_tfm *crypto_ahash_crt(struct crypto_ahash *tfm)
+{
+	return &crypto_ahash_tfm(tfm)->crt_ahash;
+}
+
+static inline unsigned int crypto_ahash_digestsize(struct crypto_ahash *tfm)
+{
+	return crypto_ahash_crt(tfm)->digestsize;
+}
+
+static inline u32 crypto_ahash_get_flags(struct crypto_ahash *tfm)
+{
+	return crypto_tfm_get_flags(crypto_ahash_tfm(tfm));
+}
+
+static inline void crypto_ahash_set_flags(struct crypto_ahash *tfm, u32 flags)
+{
+	crypto_tfm_set_flags(crypto_ahash_tfm(tfm), flags);
+}
+
+static inline void crypto_ahash_clear_flags(struct crypto_ahash *tfm, u32 flags)
+{
+	crypto_tfm_clear_flags(crypto_ahash_tfm(tfm), flags);
+}
+
+static inline struct crypto_ahash *crypto_ahash_reqtfm(
+	struct ahash_request *req)
+{
+	return __crypto_ahash_cast(req->base.tfm);
+}
+
+static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm)
+{
+	return crypto_ahash_crt(tfm)->reqsize;
+}
+
+static inline int crypto_ahash_setkey(struct crypto_ahash *tfm,
+				      const u8 *key, unsigned int keylen)
+{
+	struct ahash_tfm *crt = crypto_ahash_crt(tfm);
+
+	return crt->setkey(tfm, key, keylen);
+}
+
+static inline int crypto_ahash_digest(struct ahash_request *req)
+{
+	struct ahash_tfm *crt = crypto_ahash_crt(crypto_ahash_reqtfm(req));
+	return crt->digest(req);
+}
+
+static inline void ahash_request_set_tfm(struct ahash_request *req,
+					 struct crypto_ahash *tfm)
+{
+	req->base.tfm = crypto_ahash_tfm(tfm);
+}
+
+static inline struct ahash_request *ahash_request_alloc(
+	struct crypto_ahash *tfm, gfp_t gfp)
+{
+	struct ahash_request *req;
+
+	req = kmalloc(sizeof(struct ahash_request) +
+		      crypto_ahash_reqsize(tfm), gfp);
+
+	if (likely(req))
+		ahash_request_set_tfm(req, tfm);
+
+	return req;
+}
+
+static inline void ahash_request_free(struct ahash_request *req)
+{
+	kfree(req);
+}
+
+static inline struct ahash_request *ahash_request_cast(
+	struct crypto_async_request *req)
+{
+	return container_of(req, struct ahash_request, base);
+}
+
+static inline void ahash_request_set_callback(struct ahash_request *req,
+					      u32 flags,
+					      crypto_completion_t complete,
+					      void *data)
+{
+	req->base.complete = complete;
+	req->base.data = data;
+	req->base.flags = flags;
+}
+
+static inline void ahash_request_set_crypt(struct ahash_request *req,
+					   struct scatterlist *src, u8 *result,
+					   unsigned int nbytes)
+{
+	req->src = src;
+	req->nbytes = nbytes;
+	req->result = result;
+}
+
+#endif	/* _CRYPTO_HASH_H */
diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h
new file mode 100644
index 0000000..917ae57
--- /dev/null
+++ b/include/crypto/internal/hash.h
@@ -0,0 +1,78 @@
+/*
+ * Hash algorithms.
+ * 
+ * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 _CRYPTO_INTERNAL_HASH_H
+#define _CRYPTO_INTERNAL_HASH_H
+
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+
+struct ahash_request;
+struct scatterlist;
+
+struct crypto_hash_walk {
+	char *data;
+
+	unsigned int offset;
+	unsigned int alignmask;
+
+	struct page *pg;
+	unsigned int entrylen;
+
+	unsigned int total;
+	struct scatterlist *sg;
+
+	unsigned int flags;
+};
+
+extern const struct crypto_type crypto_ahash_type;
+
+int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err);
+int crypto_hash_walk_first(struct ahash_request *req,
+			   struct crypto_hash_walk *walk);
+
+static inline void *crypto_ahash_ctx(struct crypto_ahash *tfm)
+{
+	return crypto_tfm_ctx(&tfm->base);
+}
+
+static inline struct ahash_alg *crypto_ahash_alg(
+	struct crypto_ahash *tfm)
+{
+	return &crypto_ahash_tfm(tfm)->__crt_alg->cra_ahash;
+}
+
+static inline int ahash_enqueue_request(struct crypto_queue *queue,
+					     struct ahash_request *request)
+{
+	return crypto_enqueue_request(queue, &request->base);
+}
+
+static inline struct ahash_request *ahash_dequeue_request(
+	struct crypto_queue *queue)
+{
+	return ahash_request_cast(crypto_dequeue_request(queue));
+}
+
+static inline void *ahash_request_ctx(struct ahash_request *req)
+{
+	return req->__ctx;
+}
+
+static inline int ahash_tfm_in_queue(struct crypto_queue *queue,
+					  struct crypto_ahash *tfm)
+{
+	return crypto_tfm_in_queue(queue, crypto_ahash_tfm(tfm));
+}
+
+#endif	/* _CRYPTO_INTERNAL_HASH_H */
+
diff --git a/include/drm/Kbuild b/include/drm/Kbuild
new file mode 100644
index 0000000..82b6983
--- /dev/null
+++ b/include/drm/Kbuild
@@ -0,0 +1,10 @@
+unifdef-y += drm.h drm_sarea.h
+unifdef-y += i810_drm.h
+unifdef-y += i830_drm.h
+unifdef-y += i915_drm.h
+unifdef-y += mga_drm.h
+unifdef-y += r128_drm.h
+unifdef-y += radeon_drm.h
+unifdef-y += sis_drm.h
+unifdef-y += savage_drm.h
+unifdef-y += via_drm.h
diff --git a/drivers/char/drm/drm.h b/include/drm/drm.h
similarity index 100%
rename from drivers/char/drm/drm.h
rename to include/drm/drm.h
diff --git a/drivers/char/drm/drmP.h b/include/drm/drmP.h
similarity index 100%
rename from drivers/char/drm/drmP.h
rename to include/drm/drmP.h
diff --git a/drivers/char/drm/drm_core.h b/include/drm/drm_core.h
similarity index 100%
rename from drivers/char/drm/drm_core.h
rename to include/drm/drm_core.h
diff --git a/drivers/char/drm/drm_hashtab.h b/include/drm/drm_hashtab.h
similarity index 100%
rename from drivers/char/drm/drm_hashtab.h
rename to include/drm/drm_hashtab.h
diff --git a/drivers/char/drm/drm_memory.h b/include/drm/drm_memory.h
similarity index 100%
rename from drivers/char/drm/drm_memory.h
rename to include/drm/drm_memory.h
diff --git a/drivers/char/drm/drm_memory_debug.h b/include/drm/drm_memory_debug.h
similarity index 100%
rename from drivers/char/drm/drm_memory_debug.h
rename to include/drm/drm_memory_debug.h
diff --git a/drivers/char/drm/drm_os_linux.h b/include/drm/drm_os_linux.h
similarity index 100%
rename from drivers/char/drm/drm_os_linux.h
rename to include/drm/drm_os_linux.h
diff --git a/drivers/char/drm/drm_pciids.h b/include/drm/drm_pciids.h
similarity index 100%
rename from drivers/char/drm/drm_pciids.h
rename to include/drm/drm_pciids.h
diff --git a/drivers/char/drm/drm_sarea.h b/include/drm/drm_sarea.h
similarity index 100%
rename from drivers/char/drm/drm_sarea.h
rename to include/drm/drm_sarea.h
diff --git a/drivers/char/drm/drm_sman.h b/include/drm/drm_sman.h
similarity index 100%
rename from drivers/char/drm/drm_sman.h
rename to include/drm/drm_sman.h
diff --git a/drivers/char/drm/i810_drm.h b/include/drm/i810_drm.h
similarity index 100%
rename from drivers/char/drm/i810_drm.h
rename to include/drm/i810_drm.h
diff --git a/drivers/char/drm/i830_drm.h b/include/drm/i830_drm.h
similarity index 100%
rename from drivers/char/drm/i830_drm.h
rename to include/drm/i830_drm.h
diff --git a/drivers/char/drm/i915_drm.h b/include/drm/i915_drm.h
similarity index 100%
rename from drivers/char/drm/i915_drm.h
rename to include/drm/i915_drm.h
diff --git a/drivers/char/drm/mga_drm.h b/include/drm/mga_drm.h
similarity index 100%
rename from drivers/char/drm/mga_drm.h
rename to include/drm/mga_drm.h
diff --git a/drivers/char/drm/r128_drm.h b/include/drm/r128_drm.h
similarity index 100%
rename from drivers/char/drm/r128_drm.h
rename to include/drm/r128_drm.h
diff --git a/drivers/char/drm/radeon_drm.h b/include/drm/radeon_drm.h
similarity index 100%
rename from drivers/char/drm/radeon_drm.h
rename to include/drm/radeon_drm.h
diff --git a/drivers/char/drm/savage_drm.h b/include/drm/savage_drm.h
similarity index 100%
rename from drivers/char/drm/savage_drm.h
rename to include/drm/savage_drm.h
diff --git a/drivers/char/drm/sis_drm.h b/include/drm/sis_drm.h
similarity index 100%
rename from drivers/char/drm/sis_drm.h
rename to include/drm/sis_drm.h
diff --git a/drivers/char/drm/via_drm.h b/include/drm/via_drm.h
similarity index 100%
rename from drivers/char/drm/via_drm.h
rename to include/drm/via_drm.h
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 61c15ea..0933a14 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -64,6 +64,7 @@
 
 struct bio_set;
 struct bio;
+struct bio_integrity_payload;
 typedef void (bio_end_io_t) (struct bio *, int);
 typedef void (bio_destructor_t) (struct bio *);
 
@@ -112,6 +113,9 @@
 	atomic_t		bi_cnt;		/* pin count */
 
 	void			*bi_private;
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+	struct bio_integrity_payload *bi_integrity;  /* data integrity */
+#endif
 
 	bio_destructor_t	*bi_destructor;	/* destructor */
 };
@@ -271,6 +275,29 @@
  */
 #define bio_get(bio)	atomic_inc(&(bio)->bi_cnt)
 
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+/*
+ * bio integrity payload
+ */
+struct bio_integrity_payload {
+	struct bio		*bip_bio;	/* parent bio */
+	struct bio_vec		*bip_vec;	/* integrity data vector */
+
+	sector_t		bip_sector;	/* virtual start sector */
+
+	void			*bip_buf;	/* generated integrity data */
+	bio_end_io_t		*bip_end_io;	/* saved I/O completion fn */
+
+	int			bip_error;	/* saved I/O error */
+	unsigned int		bip_size;
+
+	unsigned short		bip_pool;	/* pool the ivec came from */
+	unsigned short		bip_vcnt;	/* # of integrity bio_vecs */
+	unsigned short		bip_idx;	/* current bip_vec index */
+
+	struct work_struct	bip_work;	/* I/O completion */
+};
+#endif /* CONFIG_BLK_DEV_INTEGRITY */
 
 /*
  * A bio_pair is used when we need to split a bio.
@@ -283,10 +310,14 @@
  *   in bio2.bi_private
  */
 struct bio_pair {
-	struct bio	bio1, bio2;
-	struct bio_vec	bv1, bv2;
-	atomic_t	cnt;
-	int		error;
+	struct bio			bio1, bio2;
+	struct bio_vec			bv1, bv2;
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+	struct bio_integrity_payload	bip1, bip2;
+	struct bio_vec			iv1, iv2;
+#endif
+	atomic_t			cnt;
+	int				error;
 };
 extern struct bio_pair *bio_split(struct bio *bi, mempool_t *pool,
 				  int first_sectors);
@@ -333,6 +364,39 @@
 				     int, int);
 extern int bio_uncopy_user(struct bio *);
 void zero_fill_bio(struct bio *bio);
+extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *);
+extern unsigned int bvec_nr_vecs(unsigned short idx);
+
+/*
+ * bio_set is used to allow other portions of the IO system to
+ * allocate their own private memory pools for bio and iovec structures.
+ * These memory pools in turn all allocate from the bio_slab
+ * and the bvec_slabs[].
+ */
+#define BIO_POOL_SIZE 2
+#define BIOVEC_NR_POOLS 6
+
+struct bio_set {
+	mempool_t *bio_pool;
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+	mempool_t *bio_integrity_pool;
+#endif
+	mempool_t *bvec_pools[BIOVEC_NR_POOLS];
+};
+
+struct biovec_slab {
+	int nr_vecs;
+	char *name;
+	struct kmem_cache *slab;
+};
+
+extern struct bio_set *fs_bio_set;
+
+/*
+ * a small number of entries is fine, not going to be performance critical.
+ * basically we just need to survive
+ */
+#define BIO_SPLIT_ENTRIES 2
 
 #ifdef CONFIG_HIGHMEM
 /*
@@ -381,5 +445,63 @@
 	__bio_kmap_irq((bio), (bio)->bi_idx, (flags))
 #define bio_kunmap_irq(buf,flags)	__bio_kunmap_irq(buf, flags)
 
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+
+#define bip_vec_idx(bip, idx)	(&(bip->bip_vec[(idx)]))
+#define bip_vec(bip)		bip_vec_idx(bip, 0)
+
+#define __bip_for_each_vec(bvl, bip, i, start_idx)			\
+	for (bvl = bip_vec_idx((bip), (start_idx)), i = (start_idx);	\
+	     i < (bip)->bip_vcnt;					\
+	     bvl++, i++)
+
+#define bip_for_each_vec(bvl, bip, i)					\
+	__bip_for_each_vec(bvl, bip, i, (bip)->bip_idx)
+
+static inline int bio_integrity(struct bio *bio)
+{
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+	return bio->bi_integrity != NULL;
+#else
+	return 0;
+#endif
+}
+
+extern struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *, gfp_t, unsigned int, struct bio_set *);
+extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, unsigned int);
+extern void bio_integrity_free(struct bio *, struct bio_set *);
+extern int bio_integrity_add_page(struct bio *, struct page *, unsigned int, unsigned int);
+extern int bio_integrity_enabled(struct bio *bio);
+extern int bio_integrity_set_tag(struct bio *, void *, unsigned int);
+extern int bio_integrity_get_tag(struct bio *, void *, unsigned int);
+extern int bio_integrity_prep(struct bio *);
+extern void bio_integrity_endio(struct bio *, int);
+extern void bio_integrity_advance(struct bio *, unsigned int);
+extern void bio_integrity_trim(struct bio *, unsigned int, unsigned int);
+extern void bio_integrity_split(struct bio *, struct bio_pair *, int);
+extern int bio_integrity_clone(struct bio *, struct bio *, struct bio_set *);
+extern int bioset_integrity_create(struct bio_set *, int);
+extern void bioset_integrity_free(struct bio_set *);
+extern void bio_integrity_init_slab(void);
+
+#else /* CONFIG_BLK_DEV_INTEGRITY */
+
+#define bio_integrity(a)		(0)
+#define bioset_integrity_create(a, b)	(0)
+#define bio_integrity_prep(a)		(0)
+#define bio_integrity_enabled(a)	(0)
+#define bio_integrity_clone(a, b, c)	(0)
+#define bioset_integrity_free(a)	do { } while (0)
+#define bio_integrity_free(a, b)	do { } while (0)
+#define bio_integrity_endio(a, b)	do { } while (0)
+#define bio_integrity_advance(a, b)	do { } while (0)
+#define bio_integrity_trim(a, b, c)	do { } while (0)
+#define bio_integrity_split(a, b, c)	do { } while (0)
+#define bio_integrity_set_tag(a, b, c)	do { } while (0)
+#define bio_integrity_get_tag(a, b, c)	do { } while (0)
+#define bio_integrity_init_slab(a)	do { } while (0)
+
+#endif /* CONFIG_BLK_DEV_INTEGRITY */
+
 #endif /* CONFIG_BLOCK */
 #endif /* __LINUX_BIO_H */
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index d2a1b71..1ffd8bf 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -23,7 +23,6 @@
 struct scsi_ioctl_command;
 
 struct request_queue;
-typedef struct request_queue request_queue_t __deprecated;
 struct elevator_queue;
 typedef struct elevator_queue elevator_t;
 struct request_pm_state;
@@ -34,12 +33,6 @@
 #define BLKDEV_MIN_RQ	4
 #define BLKDEV_MAX_RQ	128	/* Default maximum */
 
-int put_io_context(struct io_context *ioc);
-void exit_io_context(void);
-struct io_context *get_io_context(gfp_t gfp_flags, int node);
-struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-void copy_io_context(struct io_context **pdst, struct io_context **psrc);
-
 struct request;
 typedef void (rq_end_io_fn)(struct request *, int);
 
@@ -113,6 +106,7 @@
 	__REQ_ALLOCED,		/* request came from our alloc pool */
 	__REQ_RW_META,		/* metadata io request */
 	__REQ_COPY_USER,	/* contains copies of user pages */
+	__REQ_INTEGRITY,	/* integrity metadata has been remapped */
 	__REQ_NR_BITS,		/* stops here */
 };
 
@@ -135,6 +129,7 @@
 #define REQ_ALLOCED	(1 << __REQ_ALLOCED)
 #define REQ_RW_META	(1 << __REQ_RW_META)
 #define REQ_COPY_USER	(1 << __REQ_COPY_USER)
+#define REQ_INTEGRITY	(1 << __REQ_INTEGRITY)
 
 #define BLK_MAX_CDB	16
 
@@ -259,7 +254,14 @@
 typedef void (unplug_fn) (struct request_queue *);
 
 struct bio_vec;
-typedef int (merge_bvec_fn) (struct request_queue *, struct bio *, struct bio_vec *);
+struct bvec_merge_data {
+	struct block_device *bi_bdev;
+	sector_t bi_sector;
+	unsigned bi_size;
+	unsigned long bi_rw;
+};
+typedef int (merge_bvec_fn) (struct request_queue *, struct bvec_merge_data *,
+			     struct bio_vec *);
 typedef void (prepare_flush_fn) (struct request_queue *, struct request *);
 typedef void (softirq_done_fn)(struct request *);
 typedef int (dma_drain_needed_fn)(struct request *);
@@ -426,6 +428,32 @@
 	__set_bit(flag, &q->queue_flags);
 }
 
+static inline int queue_flag_test_and_clear(unsigned int flag,
+					    struct request_queue *q)
+{
+	WARN_ON_ONCE(!queue_is_locked(q));
+
+	if (test_bit(flag, &q->queue_flags)) {
+		__clear_bit(flag, &q->queue_flags);
+		return 1;
+	}
+
+	return 0;
+}
+
+static inline int queue_flag_test_and_set(unsigned int flag,
+					  struct request_queue *q)
+{
+	WARN_ON_ONCE(!queue_is_locked(q));
+
+	if (!test_bit(flag, &q->queue_flags)) {
+		__set_bit(flag, &q->queue_flags);
+		return 0;
+	}
+
+	return 1;
+}
+
 static inline void queue_flag_set(unsigned int flag, struct request_queue *q)
 {
 	WARN_ON_ONCE(!queue_is_locked(q));
@@ -676,7 +704,6 @@
 			  struct request *, int);
 extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
 				  struct request *, int, rq_end_io_fn *);
-extern int blk_verify_command(unsigned char *, int);
 extern void blk_unplug(struct request_queue *q);
 
 static inline struct request_queue *bdev_get_queue(struct block_device *bdev)
@@ -749,6 +776,7 @@
 extern void blk_queue_hardsect_size(struct request_queue *, unsigned short);
 extern void blk_queue_stack_limits(struct request_queue *t, struct request_queue *b);
 extern void blk_queue_dma_pad(struct request_queue *, unsigned int);
+extern void blk_queue_update_dma_pad(struct request_queue *, unsigned int);
 extern int blk_queue_dma_drain(struct request_queue *q,
 			       dma_drain_needed_fn *dma_drain_needed,
 			       void *buf, unsigned int size);
@@ -802,6 +830,15 @@
 
 extern int blkdev_issue_flush(struct block_device *, sector_t *);
 
+/*
+* command filter functions
+*/
+extern int blk_verify_command(struct file *file, unsigned char *cmd);
+extern int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter,
+					 unsigned char *cmd, mode_t *f_mode);
+extern int blk_register_filter(struct gendisk *disk);
+extern void blk_unregister_filter(struct gendisk *disk);
+
 #define MAX_PHYS_SEGMENTS 128
 #define MAX_HW_SEGMENTS 128
 #define SAFE_MAX_SECTORS 255
@@ -865,6 +902,105 @@
 #define MODULE_ALIAS_BLOCKDEV_MAJOR(major) \
 	MODULE_ALIAS("block-major-" __stringify(major) "-*")
 
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+
+#define INTEGRITY_FLAG_READ	2	/* verify data integrity on read */
+#define INTEGRITY_FLAG_WRITE	4	/* generate data integrity on write */
+
+struct blk_integrity_exchg {
+	void			*prot_buf;
+	void			*data_buf;
+	sector_t		sector;
+	unsigned int		data_size;
+	unsigned short		sector_size;
+	const char		*disk_name;
+};
+
+typedef void (integrity_gen_fn) (struct blk_integrity_exchg *);
+typedef int (integrity_vrfy_fn) (struct blk_integrity_exchg *);
+typedef void (integrity_set_tag_fn) (void *, void *, unsigned int);
+typedef void (integrity_get_tag_fn) (void *, void *, unsigned int);
+
+struct blk_integrity {
+	integrity_gen_fn	*generate_fn;
+	integrity_vrfy_fn	*verify_fn;
+	integrity_set_tag_fn	*set_tag_fn;
+	integrity_get_tag_fn	*get_tag_fn;
+
+	unsigned short		flags;
+	unsigned short		tuple_size;
+	unsigned short		sector_size;
+	unsigned short		tag_size;
+
+	const char		*name;
+
+	struct kobject		kobj;
+};
+
+extern int blk_integrity_register(struct gendisk *, struct blk_integrity *);
+extern void blk_integrity_unregister(struct gendisk *);
+extern int blk_integrity_compare(struct block_device *, struct block_device *);
+extern int blk_rq_map_integrity_sg(struct request *, struct scatterlist *);
+extern int blk_rq_count_integrity_sg(struct request *);
+
+static inline unsigned short blk_integrity_tuple_size(struct blk_integrity *bi)
+{
+	if (bi)
+		return bi->tuple_size;
+
+	return 0;
+}
+
+static inline struct blk_integrity *bdev_get_integrity(struct block_device *bdev)
+{
+	return bdev->bd_disk->integrity;
+}
+
+static inline unsigned int bdev_get_tag_size(struct block_device *bdev)
+{
+	struct blk_integrity *bi = bdev_get_integrity(bdev);
+
+	if (bi)
+		return bi->tag_size;
+
+	return 0;
+}
+
+static inline int bdev_integrity_enabled(struct block_device *bdev, int rw)
+{
+	struct blk_integrity *bi = bdev_get_integrity(bdev);
+
+	if (bi == NULL)
+		return 0;
+
+	if (rw == READ && bi->verify_fn != NULL &&
+	    (bi->flags & INTEGRITY_FLAG_READ))
+		return 1;
+
+	if (rw == WRITE && bi->generate_fn != NULL &&
+	    (bi->flags & INTEGRITY_FLAG_WRITE))
+		return 1;
+
+	return 0;
+}
+
+static inline int blk_integrity_rq(struct request *rq)
+{
+	return bio_integrity(rq->bio);
+}
+
+#else /* CONFIG_BLK_DEV_INTEGRITY */
+
+#define blk_integrity_rq(rq)			(0)
+#define blk_rq_count_integrity_sg(a)		(0)
+#define blk_rq_map_integrity_sg(a, b)		(0)
+#define bdev_get_integrity(a)			(0)
+#define bdev_get_tag_size(a)			(0)
+#define blk_integrity_compare(a, b)		(0)
+#define blk_integrity_register(a, b)		(0)
+#define blk_integrity_unregister(a)		do { } while (0);
+
+#endif /* CONFIG_BLK_DEV_INTEGRITY */
 
 #else /* CONFIG_BLOCK */
 /*
@@ -877,17 +1013,6 @@
 	return 0;
 }
 
-static inline void exit_io_context(void)
-{
-}
-
-struct io_context;
-static inline int put_io_context(struct io_context *ioc)
-{
-	return 1;
-}
-
-
 #endif /* CONFIG_BLOCK */
 
 #endif
diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h
index e3ef903..d084b8d 100644
--- a/include/linux/blktrace_api.h
+++ b/include/linux/blktrace_api.h
@@ -129,6 +129,7 @@
 	u32 dev;
 	struct dentry *dir;
 	struct dentry *dropped_file;
+	struct dentry *msg_file;
 	atomic_t dropped;
 };
 
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index 425824b..c43dc47 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -30,15 +30,17 @@
  */
 #define CRYPTO_ALG_TYPE_MASK		0x0000000f
 #define CRYPTO_ALG_TYPE_CIPHER		0x00000001
-#define CRYPTO_ALG_TYPE_DIGEST		0x00000002
-#define CRYPTO_ALG_TYPE_HASH		0x00000003
+#define CRYPTO_ALG_TYPE_COMPRESS	0x00000002
+#define CRYPTO_ALG_TYPE_AEAD		0x00000003
 #define CRYPTO_ALG_TYPE_BLKCIPHER	0x00000004
 #define CRYPTO_ALG_TYPE_ABLKCIPHER	0x00000005
 #define CRYPTO_ALG_TYPE_GIVCIPHER	0x00000006
-#define CRYPTO_ALG_TYPE_COMPRESS	0x00000008
-#define CRYPTO_ALG_TYPE_AEAD		0x00000009
+#define CRYPTO_ALG_TYPE_DIGEST		0x00000008
+#define CRYPTO_ALG_TYPE_HASH		0x00000009
+#define CRYPTO_ALG_TYPE_AHASH		0x0000000a
 
 #define CRYPTO_ALG_TYPE_HASH_MASK	0x0000000e
+#define CRYPTO_ALG_TYPE_AHASH_MASK	0x0000000c
 #define CRYPTO_ALG_TYPE_BLKCIPHER_MASK	0x0000000c
 
 #define CRYPTO_ALG_LARVAL		0x00000010
@@ -102,6 +104,7 @@
 struct crypto_aead;
 struct crypto_blkcipher;
 struct crypto_hash;
+struct crypto_ahash;
 struct crypto_tfm;
 struct crypto_type;
 struct aead_givcrypt_request;
@@ -131,6 +134,16 @@
 	void *__ctx[] CRYPTO_MINALIGN_ATTR;
 };
 
+struct ahash_request {
+	struct crypto_async_request base;
+
+	unsigned int nbytes;
+	struct scatterlist *src;
+	u8		   *result;
+
+	void *__ctx[] CRYPTO_MINALIGN_ATTR;
+};
+
 /**
  *	struct aead_request - AEAD request
  *	@base: Common attributes for async crypto requests
@@ -195,6 +208,17 @@
 	unsigned int ivsize;
 };
 
+struct ahash_alg {
+	int (*init)(struct ahash_request *req);
+	int (*update)(struct ahash_request *req);
+	int (*final)(struct ahash_request *req);
+	int (*digest)(struct ahash_request *req);
+	int (*setkey)(struct crypto_ahash *tfm, const u8 *key,
+			unsigned int keylen);
+
+	unsigned int digestsize;
+};
+
 struct aead_alg {
 	int (*setkey)(struct crypto_aead *tfm, const u8 *key,
 	              unsigned int keylen);
@@ -272,6 +296,7 @@
 #define cra_cipher	cra_u.cipher
 #define cra_digest	cra_u.digest
 #define cra_hash	cra_u.hash
+#define cra_ahash	cra_u.ahash
 #define cra_compress	cra_u.compress
 
 struct crypto_alg {
@@ -298,6 +323,7 @@
 		struct cipher_alg cipher;
 		struct digest_alg digest;
 		struct hash_alg hash;
+		struct ahash_alg ahash;
 		struct compress_alg compress;
 	} cra_u;
 
@@ -383,6 +409,18 @@
 	unsigned int digestsize;
 };
 
+struct ahash_tfm {
+	int (*init)(struct ahash_request *req);
+	int (*update)(struct ahash_request *req);
+	int (*final)(struct ahash_request *req);
+	int (*digest)(struct ahash_request *req);
+	int (*setkey)(struct crypto_ahash *tfm, const u8 *key,
+			unsigned int keylen);
+
+	unsigned int digestsize;
+	unsigned int reqsize;
+};
+
 struct compress_tfm {
 	int (*cot_compress)(struct crypto_tfm *tfm,
 	                    const u8 *src, unsigned int slen,
@@ -397,6 +435,7 @@
 #define crt_blkcipher	crt_u.blkcipher
 #define crt_cipher	crt_u.cipher
 #define crt_hash	crt_u.hash
+#define crt_ahash	crt_u.ahash
 #define crt_compress	crt_u.compress
 
 struct crypto_tfm {
@@ -409,6 +448,7 @@
 		struct blkcipher_tfm blkcipher;
 		struct cipher_tfm cipher;
 		struct hash_tfm hash;
+		struct ahash_tfm ahash;
 		struct compress_tfm compress;
 	} crt_u;
 	
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index ae7aec3..e878741 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -110,6 +110,14 @@
 #define GENHD_FL_SUPPRESS_PARTITION_INFO	32
 #define GENHD_FL_FAIL				64
 
+#define BLK_SCSI_MAX_CMDS	(256)
+#define BLK_SCSI_CMD_PER_LONG	(BLK_SCSI_MAX_CMDS / (sizeof(long) * 8))
+
+struct blk_scsi_cmd_filter {
+	unsigned long read_ok[BLK_SCSI_CMD_PER_LONG];
+	unsigned long write_ok[BLK_SCSI_CMD_PER_LONG];
+	struct kobject kobj;
+};
 
 struct gendisk {
 	int major;			/* major number of driver */
@@ -120,6 +128,7 @@
 	struct hd_struct **part;	/* [indexed by minor] */
 	struct block_device_operations *fops;
 	struct request_queue *queue;
+	struct blk_scsi_cmd_filter cmd_filter;
 	void *private_data;
 	sector_t capacity;
 
@@ -141,6 +150,9 @@
 	struct disk_stats dkstats;
 #endif
 	struct work_struct async_notify;
+#ifdef  CONFIG_BLK_DEV_INTEGRITY
+	struct blk_integrity *integrity;
+#endif
 };
 
 /* 
diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h
index 2b7a118..08b987b 100644
--- a/include/linux/iocontext.h
+++ b/include/linux/iocontext.h
@@ -99,4 +99,22 @@
 	return NULL;
 }
 
+#ifdef CONFIG_BLOCK
+int put_io_context(struct io_context *ioc);
+void exit_io_context(void);
+struct io_context *get_io_context(gfp_t gfp_flags, int node);
+struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
+void copy_io_context(struct io_context **pdst, struct io_context **psrc);
+#else
+static inline void exit_io_context(void)
+{
+}
+
+struct io_context;
+static inline int put_io_context(struct io_context *ioc)
+{
+	return 1;
+}
+#endif
+
 #endif
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 69b2342..c4db582 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -159,6 +159,15 @@
 
 #define AP_DEVICE_ID_MATCH_DEVICE_TYPE		0x01
 
+/* s390 css bus devices (subchannels) */
+struct css_device_id {
+	__u8 match_flags;
+	__u8 type; /* subchannel type */
+	__u16 pad2;
+	__u32 pad3;
+	kernel_ulong_t driver_data;
+};
+
 #define ACPI_ID_LEN	16 /* only 9 bytes needed here, 16 bytes are used */
 			   /* to workaround crosscompile issues */
 
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index f98501b..c6f5f9d 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -95,8 +95,12 @@
 			  struct task_struct *new_parent);
 extern void __ptrace_unlink(struct task_struct *child);
 extern void ptrace_untrace(struct task_struct *child);
-extern int ptrace_may_attach(struct task_struct *task);
-extern int __ptrace_may_attach(struct task_struct *task);
+#define PTRACE_MODE_READ   1
+#define PTRACE_MODE_ATTACH 2
+/* Returns 0 on success, -errno on denial. */
+extern int __ptrace_may_access(struct task_struct *task, unsigned int mode);
+/* Returns true on success, false on denial. */
+extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);
 
 static inline int ptrace_reparented(struct task_struct *child)
 {
diff --git a/include/linux/security.h b/include/linux/security.h
index 50737c7..31c8851 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -46,7 +46,8 @@
  */
 extern int cap_capable(struct task_struct *tsk, int cap);
 extern int cap_settime(struct timespec *ts, struct timezone *tz);
-extern int cap_ptrace(struct task_struct *parent, struct task_struct *child);
+extern int cap_ptrace(struct task_struct *parent, struct task_struct *child,
+		      unsigned int mode);
 extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
 extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
 extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
@@ -79,6 +80,7 @@
 struct xfrm_policy;
 struct xfrm_state;
 struct xfrm_user_sec_ctx;
+struct seq_file;
 
 extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);
 extern int cap_netlink_recv(struct sk_buff *skb, int cap);
@@ -289,10 +291,6 @@
  *	Update module state after a successful pivot.
  *	@old_path contains the path for the old root.
  *	@new_path contains the path for the new root.
- * @sb_get_mnt_opts:
- *	Get the security relevant mount options used for a superblock
- *	@sb the superblock to get security mount options from
- *	@opts binary data structure containing all lsm mount data
  * @sb_set_mnt_opts:
  *	Set the security relevant mount options used for a superblock
  *	@sb the superblock to set security mount options for
@@ -1170,6 +1168,7 @@
  *	attributes would be changed by the execve.
  *	@parent contains the task_struct structure for parent process.
  *	@child contains the task_struct structure for child process.
+ *	@mode contains the PTRACE_MODE flags indicating the form of access.
  *	Return 0 if permission is granted.
  * @capget:
  *	Get the @effective, @inheritable, and @permitted capability sets for
@@ -1240,11 +1239,6 @@
  *	@pages contains the number of pages.
  *	Return 0 if permission is granted.
  *
- * @register_security:
- *	allow module stacking.
- *	@name contains the name of the security module being stacked.
- *	@ops contains a pointer to the struct security_operations of the module to stack.
- *
  * @secid_to_secctx:
  *	Convert secid to security context.
  *	@secid contains the security ID.
@@ -1295,7 +1289,8 @@
 struct security_operations {
 	char name[SECURITY_NAME_MAX + 1];
 
-	int (*ptrace) (struct task_struct *parent, struct task_struct *child);
+	int (*ptrace) (struct task_struct *parent, struct task_struct *child,
+		       unsigned int mode);
 	int (*capget) (struct task_struct *target,
 		       kernel_cap_t *effective,
 		       kernel_cap_t *inheritable, kernel_cap_t *permitted);
@@ -1328,6 +1323,7 @@
 	void (*sb_free_security) (struct super_block *sb);
 	int (*sb_copy_data) (char *orig, char *copy);
 	int (*sb_kern_mount) (struct super_block *sb, void *data);
+	int (*sb_show_options) (struct seq_file *m, struct super_block *sb);
 	int (*sb_statfs) (struct dentry *dentry);
 	int (*sb_mount) (char *dev_name, struct path *path,
 			 char *type, unsigned long flags, void *data);
@@ -1343,8 +1339,6 @@
 			     struct path *new_path);
 	void (*sb_post_pivotroot) (struct path *old_path,
 				   struct path *new_path);
-	int (*sb_get_mnt_opts) (const struct super_block *sb,
-				struct security_mnt_opts *opts);
 	int (*sb_set_mnt_opts) (struct super_block *sb,
 				struct security_mnt_opts *opts);
 	void (*sb_clone_mnt_opts) (const struct super_block *oldsb,
@@ -1472,10 +1466,6 @@
 	int (*netlink_send) (struct sock *sk, struct sk_buff *skb);
 	int (*netlink_recv) (struct sk_buff *skb, int cap);
 
-	/* allow module stacking */
-	int (*register_security) (const char *name,
-				  struct security_operations *ops);
-
 	void (*d_instantiate) (struct dentry *dentry, struct inode *inode);
 
 	int (*getprocattr) (struct task_struct *p, char *name, char **value);
@@ -1565,7 +1555,6 @@
 extern int security_init(void);
 extern int security_module_enable(struct security_operations *ops);
 extern int register_security(struct security_operations *ops);
-extern int mod_reg_security(const char *name, struct security_operations *ops);
 extern struct dentry *securityfs_create_file(const char *name, mode_t mode,
 					     struct dentry *parent, void *data,
 					     const struct file_operations *fops);
@@ -1573,7 +1562,8 @@
 extern void securityfs_remove(struct dentry *dentry);
 
 /* Security operations */
-int security_ptrace(struct task_struct *parent, struct task_struct *child);
+int security_ptrace(struct task_struct *parent, struct task_struct *child,
+		    unsigned int mode);
 int security_capget(struct task_struct *target,
 		    kernel_cap_t *effective,
 		    kernel_cap_t *inheritable,
@@ -1606,6 +1596,7 @@
 void security_sb_free(struct super_block *sb);
 int security_sb_copy_data(char *orig, char *copy);
 int security_sb_kern_mount(struct super_block *sb, void *data);
+int security_sb_show_options(struct seq_file *m, struct super_block *sb);
 int security_sb_statfs(struct dentry *dentry);
 int security_sb_mount(char *dev_name, struct path *path,
 		      char *type, unsigned long flags, void *data);
@@ -1617,8 +1608,6 @@
 void security_sb_post_addmount(struct vfsmount *mnt, struct path *mountpoint);
 int security_sb_pivotroot(struct path *old_path, struct path *new_path);
 void security_sb_post_pivotroot(struct path *old_path, struct path *new_path);
-int security_sb_get_mnt_opts(const struct super_block *sb,
-				struct security_mnt_opts *opts);
 int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts);
 void security_sb_clone_mnt_opts(const struct super_block *oldsb,
 				struct super_block *newsb);
@@ -1755,9 +1744,11 @@
 	return 0;
 }
 
-static inline int security_ptrace(struct task_struct *parent, struct task_struct *child)
+static inline int security_ptrace(struct task_struct *parent,
+				  struct task_struct *child,
+				  unsigned int mode)
 {
-	return cap_ptrace(parent, child);
+	return cap_ptrace(parent, child, mode);
 }
 
 static inline int security_capget(struct task_struct *target,
@@ -1881,6 +1872,12 @@
 	return 0;
 }
 
+static inline int security_sb_show_options(struct seq_file *m,
+					   struct super_block *sb)
+{
+	return 0;
+}
+
 static inline int security_sb_statfs(struct dentry *dentry)
 {
 	return 0;
@@ -1927,12 +1924,6 @@
 static inline void security_sb_post_pivotroot(struct path *old_path,
 					      struct path *new_path)
 { }
-static inline int security_sb_get_mnt_opts(const struct super_block *sb,
-					   struct security_mnt_opts *opts)
-{
-	security_init_mnt_opts(opts);
-	return 0;
-}
 
 static inline int security_sb_set_mnt_opts(struct super_block *sb,
 					   struct security_mnt_opts *opts)
diff --git a/include/pcmcia/bulkmem.h b/include/pcmcia/bulkmem.h
deleted file mode 100644
index 6bc7472..0000000
--- a/include/pcmcia/bulkmem.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * bulkmem.h -- Definitions for bulk memory services
- *
- * 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.
- *
- * The initial developer of the original code is David A. Hinds
- * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
- * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
- *
- * (C) 1999		David A. Hinds
- */
-
-#ifndef _LINUX_BULKMEM_H
-#define _LINUX_BULKMEM_H
-
-/* For GetFirstRegion and GetNextRegion */
-typedef struct region_info_t {
-    u_int		Attributes;
-    u_int		CardOffset;
-    u_int		RegionSize;
-    u_int		AccessSpeed;
-    u_int		BlockSize;
-    u_int		PartMultiple;
-    u_char		JedecMfr, JedecInfo;
-    memory_handle_t	next;
-} region_info_t;
-
-#define REGION_TYPE		0x0001
-#define REGION_TYPE_CM		0x0000
-#define REGION_TYPE_AM		0x0001
-#define REGION_PREFETCH		0x0008
-#define REGION_CACHEABLE	0x0010
-#define REGION_BAR_MASK		0xe000
-#define REGION_BAR_SHIFT	13
-
-int pcmcia_get_first_region(struct pcmcia_device *handle, region_info_t *rgn);
-int pcmcia_get_next_region(struct pcmcia_device *handle, region_info_t *rgn);
-
-#endif /* _LINUX_BULKMEM_H */
diff --git a/include/pcmcia/cistpl.h b/include/pcmcia/cistpl.h
index d3bbb19..e2e10c1 100644
--- a/include/pcmcia/cistpl.h
+++ b/include/pcmcia/cistpl.h
@@ -595,7 +595,7 @@
 int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
 int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse);
 
-int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_t *info);
+int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, unsigned int *count);
 
 /* ... but use these wrappers instead */
 #define pcmcia_get_first_tuple(p_dev, tuple) \
diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h
index 87a260e..45d84b2 100644
--- a/include/pcmcia/cs.h
+++ b/include/pcmcia/cs.h
@@ -373,9 +373,6 @@
 
 int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, conf_reg_t *reg);
 int pcmcia_get_configuration_info(struct pcmcia_device *p_dev, config_info_t *config);
-int pcmcia_get_first_window(window_handle_t *win, win_req_t *req);
-int pcmcia_get_next_window(window_handle_t *win, win_req_t *req);
-int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status);
 int pcmcia_get_mem_page(window_handle_t win, memreq_t *req);
 int pcmcia_map_mem_page(window_handle_t win, memreq_t *req);
 int pcmcia_modify_configuration(struct pcmcia_device *p_dev, modconf_t *mod);
diff --git a/include/pcmcia/cs_types.h b/include/pcmcia/cs_types.h
index 9a6bcc4..f402a0f 100644
--- a/include/pcmcia/cs_types.h
+++ b/include/pcmcia/cs_types.h
@@ -21,7 +21,8 @@
 #include <sys/types.h>
 #endif
 
-#if defined(__arm__) || defined(__mips__) || defined(__avr32__)
+#if defined(__arm__) || defined(__mips__) || defined(__avr32__) || \
+	defined(__bfin__)
 /* This (ioaddr_t) is exposed to userspace & hence cannot be changed. */
 typedef u_int   ioaddr_t;
 #else
@@ -33,9 +34,6 @@
 typedef u_char	cisdata_t;
 typedef u_short	page_t;
 
-struct pcmcia_device;
-typedef struct pcmcia_device *client_handle_t;
-
 struct window_t;
 typedef struct window_t *window_handle_t;
 
diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h
index f047a1f..b316027 100644
--- a/include/pcmcia/ds.h
+++ b/include/pcmcia/ds.h
@@ -20,7 +20,6 @@
 #include <linux/mod_devicetable.h>
 #endif
 
-#include <pcmcia/bulkmem.h>
 #include <pcmcia/cs_types.h>
 #include <pcmcia/device_id.h>
 
@@ -51,6 +50,24 @@
     u_int		CardOffset;
 } mtd_info_t;
 
+typedef struct region_info_t {
+    u_int		Attributes;
+    u_int		CardOffset;
+    u_int		RegionSize;
+    u_int		AccessSpeed;
+    u_int		BlockSize;
+    u_int		PartMultiple;
+    u_char		JedecMfr, JedecInfo;
+    memory_handle_t	next;
+} region_info_t;
+#define REGION_TYPE		0x0001
+#define REGION_TYPE_CM		0x0000
+#define REGION_TYPE_AM		0x0001
+#define REGION_PREFETCH		0x0008
+#define REGION_CACHEABLE	0x0010
+#define REGION_BAR_MASK		0xe000
+#define REGION_BAR_SHIFT	13
+
 typedef union ds_ioctl_arg_t {
     adjust_t		adjust;
     config_info_t	config;
diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h
index f95dca0..ed919dd 100644
--- a/include/pcmcia/ss.h
+++ b/include/pcmcia/ss.h
@@ -21,7 +21,6 @@
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
-#include <pcmcia/bulkmem.h>
 #ifdef CONFIG_CARDBUS
 #include <linux/pci.h>
 #endif
@@ -136,8 +135,14 @@
 	struct resource* (*find_mem)	(unsigned long base, unsigned long num,
 					 unsigned long align, int low,
 					 struct pcmcia_socket *s);
-	int	(*adjust_resource)	(struct pcmcia_socket *s,
-					 adjust_t *adj);
+	int	(*add_io)		(struct pcmcia_socket *s,
+					 unsigned int action,
+					 unsigned long r_start,
+					 unsigned long r_end);
+	int	(*add_mem)		(struct pcmcia_socket *s,
+					 unsigned int action,
+					 unsigned long r_start,
+					 unsigned long r_end);
 	int	(*init)			(struct pcmcia_socket *s);
 	void	(*exit)			(struct pcmcia_socket *s);
 };
@@ -245,7 +250,6 @@
 
 	struct task_struct		*thread;
 	struct completion		thread_done;
-	wait_queue_head_t		thread_wait;
 	spinlock_t			thread_lock;	/* protects thread_events */
 	unsigned int			thread_events;
 
diff --git a/include/pcmcia/version.h b/include/pcmcia/version.h
deleted file mode 100644
index 5ad9c5e..0000000
--- a/include/pcmcia/version.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/* version.h 1.94 2000/10/03 17:55:48 (David Hinds) */
-
-/* This file will be removed, please don't include it */
diff --git a/include/sound/ad1843.h b/include/sound/ad1843.h
new file mode 100644
index 0000000..b236a9d
--- /dev/null
+++ b/include/sound/ad1843.h
@@ -0,0 +1,46 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ * Copyright 2008 Thomas Bogendoerfer <tsbogend@franken.de>
+ */
+
+#ifndef __SOUND_AD1843_H
+#define __SOUND_AD1843_H
+
+struct snd_ad1843 {
+	void *chip;
+	int (*read)(void *chip, int reg);
+	int (*write)(void *chip, int reg, int val);
+};
+
+#define AD1843_GAIN_RECLEV 0
+#define AD1843_GAIN_LINE   1
+#define AD1843_GAIN_LINE_2 2
+#define AD1843_GAIN_MIC    3
+#define AD1843_GAIN_PCM_0  4
+#define AD1843_GAIN_PCM_1  5
+#define AD1843_GAIN_SIZE   (AD1843_GAIN_PCM_1+1)
+
+int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id);
+int ad1843_get_gain(struct snd_ad1843 *ad1843, int id);
+int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval);
+int ad1843_get_recsrc(struct snd_ad1843 *ad1843);
+int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc);
+void ad1843_setup_dac(struct snd_ad1843 *ad1843,
+		      unsigned int id,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels);
+void ad1843_shutdown_dac(struct snd_ad1843 *ad1843,
+			 unsigned int id);
+void ad1843_setup_adc(struct snd_ad1843 *ad1843,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels);
+void ad1843_shutdown_adc(struct snd_ad1843 *ad1843);
+int ad1843_init(struct snd_ad1843 *ad1843);
+
+#endif /* __SOUND_AD1843_H */
diff --git a/include/sound/control.h b/include/sound/control.h
index 3dc1291..4721b4b 100644
--- a/include/sound/control.h
+++ b/include/sound/control.h
@@ -129,9 +129,6 @@
 #define snd_ctl_unregister_ioctl_compat(fcn)
 #endif
 
-int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control);
-int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_ctl_elem_value *control);
-
 static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)
 {
 	return id->numid - kctl->id.numid;
diff --git a/include/sound/core.h b/include/sound/core.h
index 695ee53..558b962 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -412,13 +412,13 @@
 
 #endif /* CONFIG_SND_DEBUG */
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 /**
  * snd_printdd - debug printk
  * @format: format string
  *
  * Works like snd_printk() for debugging purposes.
- * Ignored when CONFIG_SND_DEBUG_DETECT is not set.
+ * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
  */
 #define snd_printdd(format, args...) snd_printk(format, ##args)
 #else
@@ -442,7 +442,7 @@
 	unsigned short subvendor;	/* PCI subvendor ID */
 	unsigned short subdevice;	/* PCI subdevice ID */
 	int value;			/* value */
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	const char *name;		/* name of the device (optional) */
 #endif
 };
@@ -450,7 +450,7 @@
 #define _SND_PCI_QUIRK_ID(vend,dev) \
 	.subvendor = (vend), .subdevice = (dev)
 #define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 #define SND_PCI_QUIRK(vend,dev,xname,val) \
 	{_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)}
 #else
diff --git a/include/sound/cs4231-regs.h b/include/sound/cs4231-regs.h
index e8d1f3e..9264753 100644
--- a/include/sound/cs4231-regs.h
+++ b/include/sound/cs4231-regs.h
@@ -177,4 +177,12 @@
 #define CS4236_RIGHT_WAVE	0x1c	/* right wavetable serial port volume */
 #define CS4236_VERSION		0x9c	/* chip version and ID */
 
+/* definitions for extended registers - OPTI93X */
+#define OPTi931_AUX_LEFT_INPUT	0x10
+#define OPTi931_AUX_RIGHT_INPUT	0x11
+#define OPTi93X_MIC_LEFT_INPUT	0x14
+#define OPTi93X_MIC_RIGHT_INPUT	0x15
+#define OPTi93X_OUT_LEFT	0x16
+#define OPTi93X_OUT_RIGHT	0x17
+
 #endif /* __SOUND_CS4231_REGS_H */
diff --git a/include/sound/cs4231.h b/include/sound/cs4231.h
index 66055d7..f0785f9 100644
--- a/include/sound/cs4231.h
+++ b/include/sound/cs4231.h
@@ -58,6 +58,7 @@
 /* compatible, but clones */
 #define CS4231_HW_INTERWAVE     0x1000	/* InterWave chip */
 #define CS4231_HW_OPL3SA2       0x1101	/* OPL3-SA2 chip, similar to cs4231 */
+#define CS4231_HW_OPTI93X 	0x1102	/* Opti 930/931/933 */
 
 /* defines for codec.hwshare */
 #define CS4231_HWSHARE_IRQ	(1<<0)
@@ -120,6 +121,8 @@
 void snd_cs4231_mce_up(struct snd_cs4231 *chip);
 void snd_cs4231_mce_down(struct snd_cs4231 *chip);
 
+void snd_cs4231_overrange(struct snd_cs4231 *chip);
+
 irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id);
 
 const char *snd_cs4231_chip_id(struct snd_cs4231 *chip);
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 7b7b9b1..10ee28e 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -1670,6 +1670,7 @@
 	unsigned char spi_dac;      /* SPI interface for DAC */
 	unsigned char i2c_adc;      /* I2C interface for ADC */
 	unsigned char adc_1361t;    /* Use Philips 1361T ADC */
+	unsigned char invert_shared_spdif; /* analog/digital switch inverted */
 	const char *driver;
 	const char *name;
 	const char *id;		/* for backward compatibility - can be NULL if not needed */
diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h
index f023c1b..3d9afb6 100644
--- a/include/sound/seq_kernel.h
+++ b/include/sound/seq_kernel.h
@@ -105,7 +105,7 @@
 			      int cap, int type, int midi_channels, int midi_voices, char *portname);
 int snd_seq_event_port_detach(int client, int port);
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 void snd_seq_autoload_lock(void);
 void snd_seq_autoload_unlock(void);
 #else
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index a105b01..3030fdc 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -130,6 +130,13 @@
 {	.id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
 	.shift = wshift, .invert = winvert}
 
+/* generic register modifier widget */
+#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
+{	.id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \
+	.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
+	.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
+	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
+
 /* dapm kcontrol types */
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -193,6 +200,7 @@
 enum snd_soc_dapm_type;
 struct snd_soc_dapm_path;
 struct snd_soc_dapm_pin;
+struct snd_soc_dapm_route;
 
 /* dapm controls */
 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
@@ -205,25 +213,32 @@
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
 	const struct snd_soc_dapm_widget *widget);
+int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+	const struct snd_soc_dapm_widget *widget,
+	int num);
 
 /* dapm path setup */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
+int  __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
 	const char *sink_name, const char *control_name, const char *src_name);
 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
 void snd_soc_dapm_free(struct snd_soc_device *socdev);
+int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+			    const struct snd_soc_dapm_route *route, int num);
 
 /* dapm events */
 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
 	int event);
-int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event);
+int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+	enum snd_soc_bias_level level);
 
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
 
-/* dapm audio endpoint control */
-int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
-	char *pin, int status);
-int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec);
+/* dapm audio pin control and status */
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_sync(struct snd_soc_codec *codec);
 
 /* dapm widget types */
 enum snd_soc_dapm_type {
@@ -245,6 +260,18 @@
 	snd_soc_dapm_post,			/* machine specific post widget - exec last */
 };
 
+/*
+ * DAPM audio route definition.
+ *
+ * Defines an audio route originating at source via control and finishing
+ * at sink.
+ */
+struct snd_soc_dapm_route {
+	const char *sink;
+	const char *control;
+	const char *source;
+};
+
 /* dapm audio path between two widgets */
 struct snd_soc_dapm_path {
 	char *name;
@@ -277,6 +304,9 @@
 	unsigned char shift;			/* bits to shift */
 	unsigned int saved_value;		/* widget saved value */
 	unsigned int value;				/* widget current value */
+	unsigned int mask;			/* non-shifted mask */
+	unsigned int on_val;			/* on state value */
+	unsigned int off_val;			/* off state value */
 	unsigned char power:1;			/* block power status */
 	unsigned char invert:1;			/* invert the power bit */
 	unsigned char active:1;			/* active stream on DAC, ADC's */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index d3c8c03..1890d87 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -73,6 +73,15 @@
 	.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
 	.private_value = (reg_left) | ((shift) << 8)  | \
 		((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+#define SOC_DOUBLE_S8_TLV(xname, reg, min, max, tlv_array) \
+{	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p  = (tlv_array), \
+	.info   = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
+	.put    = snd_soc_put_volsw_s8, \
+	.private_value = (reg) | (((signed char)max) << 16) | \
+			 (((signed char)min) << 24) }
 #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
 {	.reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
 	.mask = xmask, .texts = xtexts }
@@ -91,6 +100,15 @@
 	.info = snd_soc_info_volsw, \
 	.get = xhandler_get, .put = xhandler_put, \
 	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) }
+#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmask, xinvert,\
+	 xhandler_get, xhandler_put, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) }
 #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_bool_ext, \
@@ -103,6 +121,24 @@
 	.private_value = (unsigned long)&xenum }
 
 /*
+ * Bias levels
+ *
+ * @ON:      Bias is fully on for audio playback and capture operations.
+ * @PREPARE: Prepare for audio operations. Called before DAPM switching for
+ *           stream start and stop operations.
+ * @STANDBY: Low power standby state when no playback/capture operations are
+ *           in progress. NOTE: The transition time between STANDBY and ON
+ *           should be as fast as possible and no longer than 10ms.
+ * @OFF:     Power Off. No restrictions on transition times.
+ */
+enum snd_soc_bias_level {
+	SND_SOC_BIAS_ON,
+	SND_SOC_BIAS_PREPARE,
+	SND_SOC_BIAS_STANDBY,
+	SND_SOC_BIAS_OFF,
+};
+
+/*
  * Digital Audio Interface (DAI) types
  */
 #define SND_SOC_DAI_AC97	0x1
@@ -185,8 +221,7 @@
 struct snd_soc_ops;
 struct snd_soc_dai_mode;
 struct snd_soc_pcm_runtime;
-struct snd_soc_codec_dai;
-struct snd_soc_cpu_dai;
+struct snd_soc_dai;
 struct snd_soc_codec;
 struct snd_soc_machine_config;
 struct soc_enum;
@@ -221,6 +256,27 @@
 	struct snd_ac97_bus_ops *ops, int num);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
+/* Digital Audio Interface clocking API.*/
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+	unsigned int freq, int dir);
+
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+	int div_id, int div);
+
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+	int pll_id, unsigned int freq_in, unsigned int freq_out);
+
+/* Digital Audio interface formatting */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
+
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int mask, int slots);
+
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
+
+/* Digital Audio Interface mute */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
+
 /*
  *Controls
  */
@@ -249,6 +305,12 @@
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo);
+int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 
 /* SoC PCM stream information */
 struct snd_soc_pcm_stream {
@@ -272,87 +334,45 @@
 	int (*trigger)(struct snd_pcm_substream *, int);
 };
 
-/* ASoC codec DAI ops */
-struct snd_soc_codec_ops {
-	/* codec DAI clocking configuration */
-	int (*set_sysclk)(struct snd_soc_codec_dai *codec_dai,
+/* ASoC DAI ops */
+struct snd_soc_dai_ops {
+	/* DAI clocking configuration */
+	int (*set_sysclk)(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir);
-	int (*set_pll)(struct snd_soc_codec_dai *codec_dai,
+	int (*set_pll)(struct snd_soc_dai *dai,
 		int pll_id, unsigned int freq_in, unsigned int freq_out);
-	int (*set_clkdiv)(struct snd_soc_codec_dai *codec_dai,
-		int div_id, int div);
+	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 
-	/* CPU DAI format configuration */
-	int (*set_fmt)(struct snd_soc_codec_dai *codec_dai,
-		unsigned int fmt);
-	int (*set_tdm_slot)(struct snd_soc_codec_dai *codec_dai,
+	/* DAI format configuration */
+	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
+	int (*set_tdm_slot)(struct snd_soc_dai *dai,
 		unsigned int mask, int slots);
-	int (*set_tristate)(struct snd_soc_codec_dai *, int tristate);
+	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
 	/* digital mute */
-	int (*digital_mute)(struct snd_soc_codec_dai *, int mute);
+	int (*digital_mute)(struct snd_soc_dai *dai, int mute);
 };
 
-/* ASoC cpu DAI ops */
-struct snd_soc_cpu_ops {
-	/* CPU DAI clocking configuration */
-	int (*set_sysclk)(struct snd_soc_cpu_dai *cpu_dai,
-		int clk_id, unsigned int freq, int dir);
-	int (*set_clkdiv)(struct snd_soc_cpu_dai *cpu_dai,
-		int div_id, int div);
-	int (*set_pll)(struct snd_soc_cpu_dai *cpu_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out);
-
-	/* CPU DAI format configuration */
-	int (*set_fmt)(struct snd_soc_cpu_dai *cpu_dai,
-		unsigned int fmt);
-	int (*set_tdm_slot)(struct snd_soc_cpu_dai *cpu_dai,
-		unsigned int mask, int slots);
-	int (*set_tristate)(struct snd_soc_cpu_dai *, int tristate);
-};
-
-/* SoC Codec DAI */
-struct snd_soc_codec_dai {
-	char *name;
-	int id;
-	unsigned char type;
-
-	/* DAI capabilities */
-	struct snd_soc_pcm_stream playback;
-	struct snd_soc_pcm_stream capture;
-
-	/* DAI runtime info */
-	struct snd_soc_codec *codec;
-	unsigned int active;
-	unsigned char pop_wait:1;
-
-	/* ops */
-	struct snd_soc_ops ops;
-	struct snd_soc_codec_ops dai_ops;
-
-	/* DAI private data */
-	void *private_data;
-};
-
-/* SoC CPU DAI */
-struct snd_soc_cpu_dai {
-
+/* SoC  DAI (Digital Audio Interface) */
+struct snd_soc_dai {
 	/* DAI description */
 	char *name;
 	unsigned int id;
 	unsigned char type;
 
 	/* DAI callbacks */
-	int (*probe)(struct platform_device *pdev);
-	void (*remove)(struct platform_device *pdev);
+	int (*probe)(struct platform_device *pdev,
+		     struct snd_soc_dai *dai);
+	void (*remove)(struct platform_device *pdev,
+		       struct snd_soc_dai *dai);
 	int (*suspend)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 	int (*resume)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 
 	/* ops */
 	struct snd_soc_ops ops;
-	struct snd_soc_cpu_ops dai_ops;
+	struct snd_soc_dai_ops dai_ops;
 
 	/* DAI capabilities */
 	struct snd_soc_pcm_stream capture;
@@ -360,7 +380,9 @@
 
 	/* DAI runtime info */
 	struct snd_pcm_runtime *runtime;
-	unsigned char active:1;
+	struct snd_soc_codec *codec;
+	unsigned int active;
+	unsigned char pop_wait:1;
 	void *dma_data;
 
 	/* DAI private data */
@@ -374,7 +396,8 @@
 	struct mutex mutex;
 
 	/* callbacks */
-	int (*dapm_event)(struct snd_soc_codec *codec, int event);
+	int (*set_bias_level)(struct snd_soc_codec *,
+			      enum snd_soc_bias_level level);
 
 	/* runtime */
 	struct snd_card *card;
@@ -396,12 +419,12 @@
 	/* dapm */
 	struct list_head dapm_widgets;
 	struct list_head dapm_paths;
-	unsigned int dapm_state;
-	unsigned int suspend_dapm_state;
+	enum snd_soc_bias_level bias_level;
+	enum snd_soc_bias_level suspend_bias_level;
 	struct delayed_work delayed_work;
 
 	/* codec DAI's */
-	struct snd_soc_codec_dai *dai;
+	struct snd_soc_dai *dai;
 	unsigned int num_dai;
 };
 
@@ -420,12 +443,12 @@
 	int (*probe)(struct platform_device *pdev);
 	int (*remove)(struct platform_device *pdev);
 	int (*suspend)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 	int (*resume)(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai);
+		struct snd_soc_dai *dai);
 
 	/* pcm creation and destruction */
-	int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *,
+	int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
 		struct snd_pcm *);
 	void (*pcm_free)(struct snd_pcm *);
 
@@ -439,8 +462,8 @@
 	char *stream_name;		/* Stream name */
 
 	/* DAI */
-	struct snd_soc_codec_dai *codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai;
+	struct snd_soc_dai *codec_dai;
+	struct snd_soc_dai *cpu_dai;
 
 	/* machine stream operations */
 	struct snd_soc_ops *ops;
@@ -467,7 +490,8 @@
 	int (*resume_post)(struct platform_device *pdev);
 
 	/* callbacks */
-	int (*dapm_event)(struct snd_soc_machine *, int event);
+	int (*set_bias_level)(struct snd_soc_machine *,
+			      enum snd_soc_bias_level level);
 
 	/* CPU <--> Codec DAI links  */
 	struct snd_soc_dai_link *dai_link;
@@ -482,6 +506,7 @@
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec_device *codec_dev;
 	struct delayed_work delayed_work;
+	struct work_struct deferred_resume_work;
 	void *codec_data;
 };
 
diff --git a/include/sound/uda1341.h b/include/sound/uda1341.h
index 2e564bf..110d5dc 100644
--- a/include/sound/uda1341.h
+++ b/include/sound/uda1341.h
@@ -15,8 +15,6 @@
  *                           features support
  */
 
-/* $Id: uda1341.h,v 1.8 2005/11/17 14:17:21 tiwai Exp $ */
-
 #define UDA1341_ALSA_NAME "snd-uda1341"
 
 /*
diff --git a/include/sound/version.h b/include/sound/version.h
index ed6fb2e..6b78aff 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
-/* include/version.h.  Generated by alsa/ksync script.  */
-#define CONFIG_SND_VERSION "1.0.16"
+/* include/version.h */
+#define CONFIG_SND_VERSION "1.0.17"
 #define CONFIG_SND_DATE ""
diff --git a/kernel/exit.c b/kernel/exit.c
index 8f6185e..ceb25878 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -13,6 +13,7 @@
 #include <linux/personality.h>
 #include <linux/tty.h>
 #include <linux/mnt_namespace.h>
+#include <linux/iocontext.h>
 #include <linux/key.h>
 #include <linux/security.h>
 #include <linux/cpu.h>
diff --git a/kernel/fork.c b/kernel/fork.c
index 19908b2..b71ccd0 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -23,6 +23,7 @@
 #include <linux/sem.h>
 #include <linux/file.h>
 #include <linux/fdtable.h>
+#include <linux/iocontext.h>
 #include <linux/key.h>
 #include <linux/binfmts.h>
 #include <linux/mman.h>
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 6c19e94..e337390 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -121,7 +121,7 @@
 	return ret;
 }
 
-int __ptrace_may_attach(struct task_struct *task)
+int __ptrace_may_access(struct task_struct *task, unsigned int mode)
 {
 	/* May we inspect the given task?
 	 * This check is used both for attaching with ptrace
@@ -148,16 +148,16 @@
 	if (!dumpable && !capable(CAP_SYS_PTRACE))
 		return -EPERM;
 
-	return security_ptrace(current, task);
+	return security_ptrace(current, task, mode);
 }
 
-int ptrace_may_attach(struct task_struct *task)
+bool ptrace_may_access(struct task_struct *task, unsigned int mode)
 {
 	int err;
 	task_lock(task);
-	err = __ptrace_may_attach(task);
+	err = __ptrace_may_access(task, mode);
 	task_unlock(task);
-	return !err;
+	return (!err ? true : false);
 }
 
 int ptrace_attach(struct task_struct *task)
@@ -195,7 +195,7 @@
 	/* the same process cannot be attached many times */
 	if (task->ptrace & PT_PTRACED)
 		goto bad;
-	retval = __ptrace_may_attach(task);
+	retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
 	if (retval)
 		goto bad;
 
@@ -494,7 +494,8 @@
 	 */
 	task_lock(current);
 	if (!(current->ptrace & PT_PTRACED)) {
-		ret = security_ptrace(current->parent, current);
+		ret = security_ptrace(current->parent, current,
+				      PTRACE_MODE_ATTACH);
 		/*
 		 * Set the ptrace bit in the process ptrace flags.
 		 */
diff --git a/mm/Kconfig b/mm/Kconfig
index 3aa819d..c4de852 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -129,7 +129,7 @@
 	bool "Allow for memory hot-add"
 	depends on SPARSEMEM || X86_64_ACPI_NUMA
 	depends on HOTPLUG && !HIBERNATION && ARCH_ENABLE_MEMORY_HOTPLUG
-	depends on (IA64 || X86 || PPC64 || SUPERH)
+	depends on (IA64 || X86 || PPC64 || SUPERH || S390)
 
 comment "Memory hotplug is currently incompatible with Software Suspend"
 	depends on SPARSEMEM && HOTPLUG && HIBERNATION
@@ -199,7 +199,7 @@
 config NR_QUICK
 	int
 	depends on QUICKLIST
-	default "2" if SUPERH
+	default "2" if SUPERH || AVR32
 	default "1"
 
 config VIRT_TO_BUS
diff --git a/mm/slub.c b/mm/slub.c
index 315c392..5f6e2c4 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -431,9 +431,8 @@
 	if (!t->addr)
 		return;
 
-	printk(KERN_ERR "INFO: %s in ", s);
-	__print_symbol("%s", (unsigned long)t->addr);
-	printk(" age=%lu cpu=%u pid=%d\n", jiffies - t->when, t->cpu, t->pid);
+	printk(KERN_ERR "INFO: %s in %pS age=%lu cpu=%u pid=%d\n",
+		s, t->addr, jiffies - t->when, t->cpu, t->pid);
 }
 
 static void print_tracking(struct kmem_cache *s, void *object)
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 7b0038f..bda7101 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -1135,8 +1135,7 @@
 		if (this)
 			kfree_skb(this);
 	}
-	if (!this)
-		printk(KERN_ERR "AF_IUCV msg tag %u not found\n", msg->tag);
+	BUG_ON(!this);
 
 	if (sk->sk_state == IUCV_CLOSING) {
 		if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
@@ -1196,7 +1195,7 @@
 	}
 	cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err);
 	if (unlikely(err)) {
-		printk(KERN_ERR "AF_IUCV needs the VM userid\n");
+		WARN_ON(err);
 		err = -EPROTONOSUPPORT;
 		goto out;
 	}
@@ -1210,7 +1209,6 @@
 	err = sock_register(&iucv_sock_family_ops);
 	if (err)
 		goto out_proto;
-	printk(KERN_INFO "AF_IUCV lowlevel driver initialized\n");
 	return 0;
 
 out_proto:
@@ -1226,8 +1224,6 @@
 	sock_unregister(PF_IUCV);
 	proto_unregister(&iucv_proto);
 	iucv_unregister(&af_iucv_handler, 0);
-
-	printk(KERN_INFO "AF_IUCV lowlevel driver unloaded\n");
 }
 
 module_init(afiucv_init);
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index 9189707..7f82b76 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -1559,16 +1559,11 @@
 
 	p = iucv_irq_data[smp_processor_id()];
 	if (p->ippathid >= iucv_max_pathid) {
-		printk(KERN_WARNING "iucv_do_int: Got interrupt with "
-		       "pathid %d > max_connections (%ld)\n",
-		       p->ippathid, iucv_max_pathid - 1);
+		WARN_ON(p->ippathid >= iucv_max_pathid);
 		iucv_sever_pathid(p->ippathid, iucv_error_no_listener);
 		return;
 	}
-	if (p->iptype  < 0x01 || p->iptype > 0x09) {
-		printk(KERN_ERR "iucv_do_int: unknown iucv interrupt\n");
-		return;
-	}
+	BUG_ON(p->iptype  < 0x01 || p->iptype > 0x09);
 	work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
 	if (!work) {
 		printk(KERN_WARNING "iucv_external_interrupt: out of memory\n");
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index cea4a79..37d5c36 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -304,6 +304,14 @@
 	return 1;
 }
 
+/* looks like: "css:tN" */
+static int do_css_entry(const char *filename,
+			struct css_device_id *id, char *alias)
+{
+	sprintf(alias, "css:t%01X", id->type);
+	return 1;
+}
+
 /* Looks like: "serio:tyNprNidNexN" */
 static int do_serio_entry(const char *filename,
 			  struct serio_device_id *id, char *alias)
@@ -680,6 +688,10 @@
 		do_table(symval, sym->st_size,
 			 sizeof(struct ap_device_id), "ap",
 			 do_ap_entry, mod);
+	else if (sym_is(symname, "__mod_css_device_table"))
+		do_table(symval, sym->st_size,
+			 sizeof(struct css_device_id), "css",
+			 do_css_entry, mod);
 	else if (sym_is(symname, "__mod_serio_device_table"))
 		do_table(symval, sym->st_size,
 			 sizeof(struct serio_device_id), "serio",
diff --git a/security/Kconfig b/security/Kconfig
index 49b51f9..62ed471 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -73,17 +73,9 @@
 	  IPSec.
 	  If you are unsure how to answer this question, answer N.
 
-config SECURITY_CAPABILITIES
-	bool "Default Linux Capabilities"
-	depends on SECURITY
-	default y
-	help
-	  This enables the "default" Linux capabilities functionality.
-	  If you are unsure how to answer this question, answer Y.
-
 config SECURITY_FILE_CAPABILITIES
 	bool "File POSIX Capabilities (EXPERIMENTAL)"
-	depends on (SECURITY=n || SECURITY_CAPABILITIES!=n) && EXPERIMENTAL
+	depends on EXPERIMENTAL
 	default n
 	help
 	  This enables filesystem capabilities, allowing you to give
diff --git a/security/Makefile b/security/Makefile
index 7ef1107..f654260 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -6,16 +6,13 @@
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 
-# if we don't select a security model, use the default capabilities
-ifneq ($(CONFIG_SECURITY),y)
+# always enable default capabilities
 obj-y		+= commoncap.o
-endif
 
 # Object file lists
-obj-$(CONFIG_SECURITY)			+= security.o dummy.o inode.o
+obj-$(CONFIG_SECURITY)			+= security.o capability.o inode.o
 # Must precede capability.o in order to stack properly.
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
-obj-$(CONFIG_SECURITY_SMACK)		+= commoncap.o smack/built-in.o
-obj-$(CONFIG_SECURITY_CAPABILITIES)	+= commoncap.o capability.o
-obj-$(CONFIG_SECURITY_ROOTPLUG)		+= commoncap.o root_plug.o
+obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
+obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
diff --git a/security/capability.c b/security/capability.c
index 38ac54e..5b01c0b 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -1,6 +1,8 @@
 /*
  *  Capabilities Linux Security Module
  *
+ *  This is the default security module in case no other module is loaded.
+ *
  *	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
@@ -8,75 +10,988 @@
  *
  */
 
-#include <linux/init.h>
-#include <linux/kernel.h>
 #include <linux/security.h>
-#include <linux/file.h>
-#include <linux/mm.h>
-#include <linux/mman.h>
-#include <linux/pagemap.h>
-#include <linux/swap.h>
-#include <linux/skbuff.h>
-#include <linux/netlink.h>
-#include <linux/ptrace.h>
-#include <linux/moduleparam.h>
 
-static struct security_operations capability_ops = {
-	.ptrace =			cap_ptrace,
-	.capget =			cap_capget,
-	.capset_check =			cap_capset_check,
-	.capset_set =			cap_capset_set,
-	.capable =			cap_capable,
-	.settime =			cap_settime,
-	.netlink_send =			cap_netlink_send,
-	.netlink_recv =			cap_netlink_recv,
-
-	.bprm_apply_creds =		cap_bprm_apply_creds,
-	.bprm_set_security =		cap_bprm_set_security,
-	.bprm_secureexec =		cap_bprm_secureexec,
-
-	.inode_setxattr =		cap_inode_setxattr,
-	.inode_removexattr =		cap_inode_removexattr,
-	.inode_need_killpriv =		cap_inode_need_killpriv,
-	.inode_killpriv =		cap_inode_killpriv,
-
-	.task_setscheduler =		cap_task_setscheduler,
-	.task_setioprio =		cap_task_setioprio,
-	.task_setnice =			cap_task_setnice,
-	.task_post_setuid =		cap_task_post_setuid,
-	.task_prctl =                   cap_task_prctl,
-	.task_reparent_to_init =	cap_task_reparent_to_init,
-
-	.syslog =                       cap_syslog,
-
-	.vm_enough_memory =             cap_vm_enough_memory,
-};
-
-/* flag to keep track of how we were registered */
-static int secondary;
-
-static int capability_disable;
-module_param_named(disable, capability_disable, int, 0);
-
-static int __init capability_init (void)
+static int cap_acct(struct file *file)
 {
-	if (capability_disable) {
-		printk(KERN_INFO "Capabilities disabled at initialization\n");
-		return 0;
-	}
-	/* register ourselves with the security framework */
-	if (register_security (&capability_ops)) {
-		/* try registering with primary module */
-		if (mod_reg_security (KBUILD_MODNAME, &capability_ops)) {
-			printk (KERN_INFO "Failure registering capabilities "
-				"with primary security module.\n");
-			return -EINVAL;
-		}
-		secondary = 1;
-	}
-	printk (KERN_INFO "Capability LSM initialized%s\n",
-		secondary ? " as secondary" : "");
 	return 0;
 }
 
-security_initcall (capability_init);
+static int cap_sysctl(ctl_table *table, int op)
+{
+	return 0;
+}
+
+static int cap_quotactl(int cmds, int type, int id, struct super_block *sb)
+{
+	return 0;
+}
+
+static int cap_quota_on(struct dentry *dentry)
+{
+	return 0;
+}
+
+static int cap_bprm_alloc_security(struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static void cap_bprm_free_security(struct linux_binprm *bprm)
+{
+}
+
+static void cap_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+}
+
+static int cap_bprm_check_security(struct linux_binprm *bprm)
+{
+	return 0;
+}
+
+static int cap_sb_alloc_security(struct super_block *sb)
+{
+	return 0;
+}
+
+static void cap_sb_free_security(struct super_block *sb)
+{
+}
+
+static int cap_sb_copy_data(char *orig, char *copy)
+{
+	return 0;
+}
+
+static int cap_sb_kern_mount(struct super_block *sb, void *data)
+{
+	return 0;
+}
+
+static int cap_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+	return 0;
+}
+
+static int cap_sb_statfs(struct dentry *dentry)
+{
+	return 0;
+}
+
+static int cap_sb_mount(char *dev_name, struct path *path, char *type,
+			unsigned long flags, void *data)
+{
+	return 0;
+}
+
+static int cap_sb_check_sb(struct vfsmount *mnt, struct path *path)
+{
+	return 0;
+}
+
+static int cap_sb_umount(struct vfsmount *mnt, int flags)
+{
+	return 0;
+}
+
+static void cap_sb_umount_close(struct vfsmount *mnt)
+{
+}
+
+static void cap_sb_umount_busy(struct vfsmount *mnt)
+{
+}
+
+static void cap_sb_post_remount(struct vfsmount *mnt, unsigned long flags,
+				void *data)
+{
+}
+
+static void cap_sb_post_addmount(struct vfsmount *mnt, struct path *path)
+{
+}
+
+static int cap_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+	return 0;
+}
+
+static void cap_sb_post_pivotroot(struct path *old_path, struct path *new_path)
+{
+}
+
+static int cap_sb_set_mnt_opts(struct super_block *sb,
+			       struct security_mnt_opts *opts)
+{
+	if (unlikely(opts->num_mnt_opts))
+		return -EOPNOTSUPP;
+	return 0;
+}
+
+static void cap_sb_clone_mnt_opts(const struct super_block *oldsb,
+				  struct super_block *newsb)
+{
+}
+
+static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
+{
+	return 0;
+}
+
+static int cap_inode_alloc_security(struct inode *inode)
+{
+	return 0;
+}
+
+static void cap_inode_free_security(struct inode *inode)
+{
+}
+
+static int cap_inode_init_security(struct inode *inode, struct inode *dir,
+				   char **name, void **value, size_t *len)
+{
+	return -EOPNOTSUPP;
+}
+
+static int cap_inode_create(struct inode *inode, struct dentry *dentry,
+			    int mask)
+{
+	return 0;
+}
+
+static int cap_inode_link(struct dentry *old_dentry, struct inode *inode,
+			  struct dentry *new_dentry)
+{
+	return 0;
+}
+
+static int cap_inode_unlink(struct inode *inode, struct dentry *dentry)
+{
+	return 0;
+}
+
+static int cap_inode_symlink(struct inode *inode, struct dentry *dentry,
+			     const char *name)
+{
+	return 0;
+}
+
+static int cap_inode_mkdir(struct inode *inode, struct dentry *dentry,
+			   int mask)
+{
+	return 0;
+}
+
+static int cap_inode_rmdir(struct inode *inode, struct dentry *dentry)
+{
+	return 0;
+}
+
+static int cap_inode_mknod(struct inode *inode, struct dentry *dentry,
+			   int mode, dev_t dev)
+{
+	return 0;
+}
+
+static int cap_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
+			    struct inode *new_inode, struct dentry *new_dentry)
+{
+	return 0;
+}
+
+static int cap_inode_readlink(struct dentry *dentry)
+{
+	return 0;
+}
+
+static int cap_inode_follow_link(struct dentry *dentry,
+				 struct nameidata *nameidata)
+{
+	return 0;
+}
+
+static int cap_inode_permission(struct inode *inode, int mask,
+				struct nameidata *nd)
+{
+	return 0;
+}
+
+static int cap_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+	return 0;
+}
+
+static int cap_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+	return 0;
+}
+
+static void cap_inode_delete(struct inode *ino)
+{
+}
+
+static void cap_inode_post_setxattr(struct dentry *dentry, const char *name,
+				    const void *value, size_t size, int flags)
+{
+}
+
+static int cap_inode_getxattr(struct dentry *dentry, const char *name)
+{
+	return 0;
+}
+
+static int cap_inode_listxattr(struct dentry *dentry)
+{
+	return 0;
+}
+
+static int cap_inode_getsecurity(const struct inode *inode, const char *name,
+				 void **buffer, bool alloc)
+{
+	return -EOPNOTSUPP;
+}
+
+static int cap_inode_setsecurity(struct inode *inode, const char *name,
+				 const void *value, size_t size, int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+static int cap_inode_listsecurity(struct inode *inode, char *buffer,
+				  size_t buffer_size)
+{
+	return 0;
+}
+
+static void cap_inode_getsecid(const struct inode *inode, u32 *secid)
+{
+	*secid = 0;
+}
+
+static int cap_file_permission(struct file *file, int mask)
+{
+	return 0;
+}
+
+static int cap_file_alloc_security(struct file *file)
+{
+	return 0;
+}
+
+static void cap_file_free_security(struct file *file)
+{
+}
+
+static int cap_file_ioctl(struct file *file, unsigned int command,
+			  unsigned long arg)
+{
+	return 0;
+}
+
+static int cap_file_mmap(struct file *file, unsigned long reqprot,
+			 unsigned long prot, unsigned long flags,
+			 unsigned long addr, unsigned long addr_only)
+{
+	if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
+		return -EACCES;
+	return 0;
+}
+
+static int cap_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+			     unsigned long prot)
+{
+	return 0;
+}
+
+static int cap_file_lock(struct file *file, unsigned int cmd)
+{
+	return 0;
+}
+
+static int cap_file_fcntl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
+{
+	return 0;
+}
+
+static int cap_file_set_fowner(struct file *file)
+{
+	return 0;
+}
+
+static int cap_file_send_sigiotask(struct task_struct *tsk,
+				   struct fown_struct *fown, int sig)
+{
+	return 0;
+}
+
+static int cap_file_receive(struct file *file)
+{
+	return 0;
+}
+
+static int cap_dentry_open(struct file *file)
+{
+	return 0;
+}
+
+static int cap_task_create(unsigned long clone_flags)
+{
+	return 0;
+}
+
+static int cap_task_alloc_security(struct task_struct *p)
+{
+	return 0;
+}
+
+static void cap_task_free_security(struct task_struct *p)
+{
+}
+
+static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+	return 0;
+}
+
+static int cap_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
+{
+	return 0;
+}
+
+static int cap_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+	return 0;
+}
+
+static int cap_task_getpgid(struct task_struct *p)
+{
+	return 0;
+}
+
+static int cap_task_getsid(struct task_struct *p)
+{
+	return 0;
+}
+
+static void cap_task_getsecid(struct task_struct *p, u32 *secid)
+{
+	*secid = 0;
+}
+
+static int cap_task_setgroups(struct group_info *group_info)
+{
+	return 0;
+}
+
+static int cap_task_getioprio(struct task_struct *p)
+{
+	return 0;
+}
+
+static int cap_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
+{
+	return 0;
+}
+
+static int cap_task_getscheduler(struct task_struct *p)
+{
+	return 0;
+}
+
+static int cap_task_movememory(struct task_struct *p)
+{
+	return 0;
+}
+
+static int cap_task_wait(struct task_struct *p)
+{
+	return 0;
+}
+
+static int cap_task_kill(struct task_struct *p, struct siginfo *info,
+			 int sig, u32 secid)
+{
+	return 0;
+}
+
+static void cap_task_to_inode(struct task_struct *p, struct inode *inode)
+{
+}
+
+static int cap_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+	return 0;
+}
+
+static void cap_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
+{
+	*secid = 0;
+}
+
+static int cap_msg_msg_alloc_security(struct msg_msg *msg)
+{
+	return 0;
+}
+
+static void cap_msg_msg_free_security(struct msg_msg *msg)
+{
+}
+
+static int cap_msg_queue_alloc_security(struct msg_queue *msq)
+{
+	return 0;
+}
+
+static void cap_msg_queue_free_security(struct msg_queue *msq)
+{
+}
+
+static int cap_msg_queue_associate(struct msg_queue *msq, int msqflg)
+{
+	return 0;
+}
+
+static int cap_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+	return 0;
+}
+
+static int cap_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
+				int msgflg)
+{
+	return 0;
+}
+
+static int cap_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+				struct task_struct *target, long type, int mode)
+{
+	return 0;
+}
+
+static int cap_shm_alloc_security(struct shmid_kernel *shp)
+{
+	return 0;
+}
+
+static void cap_shm_free_security(struct shmid_kernel *shp)
+{
+}
+
+static int cap_shm_associate(struct shmid_kernel *shp, int shmflg)
+{
+	return 0;
+}
+
+static int cap_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+	return 0;
+}
+
+static int cap_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr,
+			 int shmflg)
+{
+	return 0;
+}
+
+static int cap_sem_alloc_security(struct sem_array *sma)
+{
+	return 0;
+}
+
+static void cap_sem_free_security(struct sem_array *sma)
+{
+}
+
+static int cap_sem_associate(struct sem_array *sma, int semflg)
+{
+	return 0;
+}
+
+static int cap_sem_semctl(struct sem_array *sma, int cmd)
+{
+	return 0;
+}
+
+static int cap_sem_semop(struct sem_array *sma, struct sembuf *sops,
+			 unsigned nsops, int alter)
+{
+	return 0;
+}
+
+#ifdef CONFIG_SECURITY_NETWORK
+static int cap_unix_stream_connect(struct socket *sock, struct socket *other,
+				   struct sock *newsk)
+{
+	return 0;
+}
+
+static int cap_unix_may_send(struct socket *sock, struct socket *other)
+{
+	return 0;
+}
+
+static int cap_socket_create(int family, int type, int protocol, int kern)
+{
+	return 0;
+}
+
+static int cap_socket_post_create(struct socket *sock, int family, int type,
+				  int protocol, int kern)
+{
+	return 0;
+}
+
+static int cap_socket_bind(struct socket *sock, struct sockaddr *address,
+			   int addrlen)
+{
+	return 0;
+}
+
+static int cap_socket_connect(struct socket *sock, struct sockaddr *address,
+			      int addrlen)
+{
+	return 0;
+}
+
+static int cap_socket_listen(struct socket *sock, int backlog)
+{
+	return 0;
+}
+
+static int cap_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	return 0;
+}
+
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
+static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
+{
+	return 0;
+}
+
+static int cap_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+			      int size, int flags)
+{
+	return 0;
+}
+
+static int cap_socket_getsockname(struct socket *sock)
+{
+	return 0;
+}
+
+static int cap_socket_getpeername(struct socket *sock)
+{
+	return 0;
+}
+
+static int cap_socket_setsockopt(struct socket *sock, int level, int optname)
+{
+	return 0;
+}
+
+static int cap_socket_getsockopt(struct socket *sock, int level, int optname)
+{
+	return 0;
+}
+
+static int cap_socket_shutdown(struct socket *sock, int how)
+{
+	return 0;
+}
+
+static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+	return 0;
+}
+
+static int cap_socket_getpeersec_stream(struct socket *sock,
+					char __user *optval,
+					int __user *optlen, unsigned len)
+{
+	return -ENOPROTOOPT;
+}
+
+static int cap_socket_getpeersec_dgram(struct socket *sock,
+				       struct sk_buff *skb, u32 *secid)
+{
+	return -ENOPROTOOPT;
+}
+
+static int cap_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+{
+	return 0;
+}
+
+static void cap_sk_free_security(struct sock *sk)
+{
+}
+
+static void cap_sk_clone_security(const struct sock *sk, struct sock *newsk)
+{
+}
+
+static void cap_sk_getsecid(struct sock *sk, u32 *secid)
+{
+}
+
+static void cap_sock_graft(struct sock *sk, struct socket *parent)
+{
+}
+
+static int cap_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+				 struct request_sock *req)
+{
+	return 0;
+}
+
+static void cap_inet_csk_clone(struct sock *newsk,
+			       const struct request_sock *req)
+{
+}
+
+static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb)
+{
+}
+
+static void cap_req_classify_flow(const struct request_sock *req,
+				  struct flowi *fl)
+{
+}
+#endif	/* CONFIG_SECURITY_NETWORK */
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+static int cap_xfrm_policy_alloc_security(struct xfrm_sec_ctx **ctxp,
+					  struct xfrm_user_sec_ctx *sec_ctx)
+{
+	return 0;
+}
+
+static int cap_xfrm_policy_clone_security(struct xfrm_sec_ctx *old_ctx,
+					  struct xfrm_sec_ctx **new_ctxp)
+{
+	return 0;
+}
+
+static void cap_xfrm_policy_free_security(struct xfrm_sec_ctx *ctx)
+{
+}
+
+static int cap_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)
+{
+	return 0;
+}
+
+static int cap_xfrm_state_alloc_security(struct xfrm_state *x,
+					 struct xfrm_user_sec_ctx *sec_ctx,
+					 u32 secid)
+{
+	return 0;
+}
+
+static void cap_xfrm_state_free_security(struct xfrm_state *x)
+{
+}
+
+static int cap_xfrm_state_delete_security(struct xfrm_state *x)
+{
+	return 0;
+}
+
+static int cap_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 sk_sid, u8 dir)
+{
+	return 0;
+}
+
+static int cap_xfrm_state_pol_flow_match(struct xfrm_state *x,
+					 struct xfrm_policy *xp,
+					 struct flowi *fl)
+{
+	return 1;
+}
+
+static int cap_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall)
+{
+	return 0;
+}
+
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+static void cap_d_instantiate(struct dentry *dentry, struct inode *inode)
+{
+}
+
+static int cap_getprocattr(struct task_struct *p, char *name, char **value)
+{
+	return -EINVAL;
+}
+
+static int cap_setprocattr(struct task_struct *p, char *name, void *value,
+			   size_t size)
+{
+	return -EINVAL;
+}
+
+static int cap_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+	return -EOPNOTSUPP;
+}
+
+static int cap_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
+{
+	return -EOPNOTSUPP;
+}
+
+static void cap_release_secctx(char *secdata, u32 seclen)
+{
+}
+
+#ifdef CONFIG_KEYS
+static int cap_key_alloc(struct key *key, struct task_struct *ctx,
+			 unsigned long flags)
+{
+	return 0;
+}
+
+static void cap_key_free(struct key *key)
+{
+}
+
+static int cap_key_permission(key_ref_t key_ref, struct task_struct *context,
+			      key_perm_t perm)
+{
+	return 0;
+}
+
+static int cap_key_getsecurity(struct key *key, char **_buffer)
+{
+	*_buffer = NULL;
+	return 0;
+}
+
+#endif /* CONFIG_KEYS */
+
+#ifdef CONFIG_AUDIT
+static int cap_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
+{
+	return 0;
+}
+
+static int cap_audit_rule_known(struct audit_krule *krule)
+{
+	return 0;
+}
+
+static int cap_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
+				struct audit_context *actx)
+{
+	return 0;
+}
+
+static void cap_audit_rule_free(void *lsmrule)
+{
+}
+#endif /* CONFIG_AUDIT */
+
+struct security_operations default_security_ops = {
+	.name	= "default",
+};
+
+#define set_to_cap_if_null(ops, function)				\
+	do {								\
+		if (!ops->function) {					\
+			ops->function = cap_##function;			\
+			pr_debug("Had to override the " #function	\
+				 " security operation with the default.\n");\
+			}						\
+	} while (0)
+
+void security_fixup_ops(struct security_operations *ops)
+{
+	set_to_cap_if_null(ops, ptrace);
+	set_to_cap_if_null(ops, capget);
+	set_to_cap_if_null(ops, capset_check);
+	set_to_cap_if_null(ops, capset_set);
+	set_to_cap_if_null(ops, acct);
+	set_to_cap_if_null(ops, capable);
+	set_to_cap_if_null(ops, quotactl);
+	set_to_cap_if_null(ops, quota_on);
+	set_to_cap_if_null(ops, sysctl);
+	set_to_cap_if_null(ops, syslog);
+	set_to_cap_if_null(ops, settime);
+	set_to_cap_if_null(ops, vm_enough_memory);
+	set_to_cap_if_null(ops, bprm_alloc_security);
+	set_to_cap_if_null(ops, bprm_free_security);
+	set_to_cap_if_null(ops, bprm_apply_creds);
+	set_to_cap_if_null(ops, bprm_post_apply_creds);
+	set_to_cap_if_null(ops, bprm_set_security);
+	set_to_cap_if_null(ops, bprm_check_security);
+	set_to_cap_if_null(ops, bprm_secureexec);
+	set_to_cap_if_null(ops, sb_alloc_security);
+	set_to_cap_if_null(ops, sb_free_security);
+	set_to_cap_if_null(ops, sb_copy_data);
+	set_to_cap_if_null(ops, sb_kern_mount);
+	set_to_cap_if_null(ops, sb_show_options);
+	set_to_cap_if_null(ops, sb_statfs);
+	set_to_cap_if_null(ops, sb_mount);
+	set_to_cap_if_null(ops, sb_check_sb);
+	set_to_cap_if_null(ops, sb_umount);
+	set_to_cap_if_null(ops, sb_umount_close);
+	set_to_cap_if_null(ops, sb_umount_busy);
+	set_to_cap_if_null(ops, sb_post_remount);
+	set_to_cap_if_null(ops, sb_post_addmount);
+	set_to_cap_if_null(ops, sb_pivotroot);
+	set_to_cap_if_null(ops, sb_post_pivotroot);
+	set_to_cap_if_null(ops, sb_set_mnt_opts);
+	set_to_cap_if_null(ops, sb_clone_mnt_opts);
+	set_to_cap_if_null(ops, sb_parse_opts_str);
+	set_to_cap_if_null(ops, inode_alloc_security);
+	set_to_cap_if_null(ops, inode_free_security);
+	set_to_cap_if_null(ops, inode_init_security);
+	set_to_cap_if_null(ops, inode_create);
+	set_to_cap_if_null(ops, inode_link);
+	set_to_cap_if_null(ops, inode_unlink);
+	set_to_cap_if_null(ops, inode_symlink);
+	set_to_cap_if_null(ops, inode_mkdir);
+	set_to_cap_if_null(ops, inode_rmdir);
+	set_to_cap_if_null(ops, inode_mknod);
+	set_to_cap_if_null(ops, inode_rename);
+	set_to_cap_if_null(ops, inode_readlink);
+	set_to_cap_if_null(ops, inode_follow_link);
+	set_to_cap_if_null(ops, inode_permission);
+	set_to_cap_if_null(ops, inode_setattr);
+	set_to_cap_if_null(ops, inode_getattr);
+	set_to_cap_if_null(ops, inode_delete);
+	set_to_cap_if_null(ops, inode_setxattr);
+	set_to_cap_if_null(ops, inode_post_setxattr);
+	set_to_cap_if_null(ops, inode_getxattr);
+	set_to_cap_if_null(ops, inode_listxattr);
+	set_to_cap_if_null(ops, inode_removexattr);
+	set_to_cap_if_null(ops, inode_need_killpriv);
+	set_to_cap_if_null(ops, inode_killpriv);
+	set_to_cap_if_null(ops, inode_getsecurity);
+	set_to_cap_if_null(ops, inode_setsecurity);
+	set_to_cap_if_null(ops, inode_listsecurity);
+	set_to_cap_if_null(ops, inode_getsecid);
+	set_to_cap_if_null(ops, file_permission);
+	set_to_cap_if_null(ops, file_alloc_security);
+	set_to_cap_if_null(ops, file_free_security);
+	set_to_cap_if_null(ops, file_ioctl);
+	set_to_cap_if_null(ops, file_mmap);
+	set_to_cap_if_null(ops, file_mprotect);
+	set_to_cap_if_null(ops, file_lock);
+	set_to_cap_if_null(ops, file_fcntl);
+	set_to_cap_if_null(ops, file_set_fowner);
+	set_to_cap_if_null(ops, file_send_sigiotask);
+	set_to_cap_if_null(ops, file_receive);
+	set_to_cap_if_null(ops, dentry_open);
+	set_to_cap_if_null(ops, task_create);
+	set_to_cap_if_null(ops, task_alloc_security);
+	set_to_cap_if_null(ops, task_free_security);
+	set_to_cap_if_null(ops, task_setuid);
+	set_to_cap_if_null(ops, task_post_setuid);
+	set_to_cap_if_null(ops, task_setgid);
+	set_to_cap_if_null(ops, task_setpgid);
+	set_to_cap_if_null(ops, task_getpgid);
+	set_to_cap_if_null(ops, task_getsid);
+	set_to_cap_if_null(ops, task_getsecid);
+	set_to_cap_if_null(ops, task_setgroups);
+	set_to_cap_if_null(ops, task_setnice);
+	set_to_cap_if_null(ops, task_setioprio);
+	set_to_cap_if_null(ops, task_getioprio);
+	set_to_cap_if_null(ops, task_setrlimit);
+	set_to_cap_if_null(ops, task_setscheduler);
+	set_to_cap_if_null(ops, task_getscheduler);
+	set_to_cap_if_null(ops, task_movememory);
+	set_to_cap_if_null(ops, task_wait);
+	set_to_cap_if_null(ops, task_kill);
+	set_to_cap_if_null(ops, task_prctl);
+	set_to_cap_if_null(ops, task_reparent_to_init);
+	set_to_cap_if_null(ops, task_to_inode);
+	set_to_cap_if_null(ops, ipc_permission);
+	set_to_cap_if_null(ops, ipc_getsecid);
+	set_to_cap_if_null(ops, msg_msg_alloc_security);
+	set_to_cap_if_null(ops, msg_msg_free_security);
+	set_to_cap_if_null(ops, msg_queue_alloc_security);
+	set_to_cap_if_null(ops, msg_queue_free_security);
+	set_to_cap_if_null(ops, msg_queue_associate);
+	set_to_cap_if_null(ops, msg_queue_msgctl);
+	set_to_cap_if_null(ops, msg_queue_msgsnd);
+	set_to_cap_if_null(ops, msg_queue_msgrcv);
+	set_to_cap_if_null(ops, shm_alloc_security);
+	set_to_cap_if_null(ops, shm_free_security);
+	set_to_cap_if_null(ops, shm_associate);
+	set_to_cap_if_null(ops, shm_shmctl);
+	set_to_cap_if_null(ops, shm_shmat);
+	set_to_cap_if_null(ops, sem_alloc_security);
+	set_to_cap_if_null(ops, sem_free_security);
+	set_to_cap_if_null(ops, sem_associate);
+	set_to_cap_if_null(ops, sem_semctl);
+	set_to_cap_if_null(ops, sem_semop);
+	set_to_cap_if_null(ops, netlink_send);
+	set_to_cap_if_null(ops, netlink_recv);
+	set_to_cap_if_null(ops, d_instantiate);
+	set_to_cap_if_null(ops, getprocattr);
+	set_to_cap_if_null(ops, setprocattr);
+	set_to_cap_if_null(ops, secid_to_secctx);
+	set_to_cap_if_null(ops, secctx_to_secid);
+	set_to_cap_if_null(ops, release_secctx);
+#ifdef CONFIG_SECURITY_NETWORK
+	set_to_cap_if_null(ops, unix_stream_connect);
+	set_to_cap_if_null(ops, unix_may_send);
+	set_to_cap_if_null(ops, socket_create);
+	set_to_cap_if_null(ops, socket_post_create);
+	set_to_cap_if_null(ops, socket_bind);
+	set_to_cap_if_null(ops, socket_connect);
+	set_to_cap_if_null(ops, socket_listen);
+	set_to_cap_if_null(ops, socket_accept);
+	set_to_cap_if_null(ops, socket_post_accept);
+	set_to_cap_if_null(ops, socket_sendmsg);
+	set_to_cap_if_null(ops, socket_recvmsg);
+	set_to_cap_if_null(ops, socket_getsockname);
+	set_to_cap_if_null(ops, socket_getpeername);
+	set_to_cap_if_null(ops, socket_setsockopt);
+	set_to_cap_if_null(ops, socket_getsockopt);
+	set_to_cap_if_null(ops, socket_shutdown);
+	set_to_cap_if_null(ops, socket_sock_rcv_skb);
+	set_to_cap_if_null(ops, socket_getpeersec_stream);
+	set_to_cap_if_null(ops, socket_getpeersec_dgram);
+	set_to_cap_if_null(ops, sk_alloc_security);
+	set_to_cap_if_null(ops, sk_free_security);
+	set_to_cap_if_null(ops, sk_clone_security);
+	set_to_cap_if_null(ops, sk_getsecid);
+	set_to_cap_if_null(ops, sock_graft);
+	set_to_cap_if_null(ops, inet_conn_request);
+	set_to_cap_if_null(ops, inet_csk_clone);
+	set_to_cap_if_null(ops, inet_conn_established);
+	set_to_cap_if_null(ops, req_classify_flow);
+#endif	/* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+	set_to_cap_if_null(ops, xfrm_policy_alloc_security);
+	set_to_cap_if_null(ops, xfrm_policy_clone_security);
+	set_to_cap_if_null(ops, xfrm_policy_free_security);
+	set_to_cap_if_null(ops, xfrm_policy_delete_security);
+	set_to_cap_if_null(ops, xfrm_state_alloc_security);
+	set_to_cap_if_null(ops, xfrm_state_free_security);
+	set_to_cap_if_null(ops, xfrm_state_delete_security);
+	set_to_cap_if_null(ops, xfrm_policy_lookup);
+	set_to_cap_if_null(ops, xfrm_state_pol_flow_match);
+	set_to_cap_if_null(ops, xfrm_decode_session);
+#endif	/* CONFIG_SECURITY_NETWORK_XFRM */
+#ifdef CONFIG_KEYS
+	set_to_cap_if_null(ops, key_alloc);
+	set_to_cap_if_null(ops, key_free);
+	set_to_cap_if_null(ops, key_permission);
+	set_to_cap_if_null(ops, key_getsecurity);
+#endif	/* CONFIG_KEYS */
+#ifdef CONFIG_AUDIT
+	set_to_cap_if_null(ops, audit_rule_init);
+	set_to_cap_if_null(ops, audit_rule_known);
+	set_to_cap_if_null(ops, audit_rule_match);
+	set_to_cap_if_null(ops, audit_rule_free);
+#endif
+}
diff --git a/security/commoncap.c b/security/commoncap.c
index 33d3433..0b6537a 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -63,7 +63,8 @@
 	return 0;
 }
 
-int cap_ptrace (struct task_struct *parent, struct task_struct *child)
+int cap_ptrace (struct task_struct *parent, struct task_struct *child,
+		unsigned int mode)
 {
 	/* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
 	if (!cap_issubset(child->cap_permitted, parent->cap_permitted) &&
diff --git a/security/dummy.c b/security/dummy.c
deleted file mode 100644
index b891688..0000000
--- a/security/dummy.c
+++ /dev/null
@@ -1,1251 +0,0 @@
-/*
- * Stub functions for the default security function pointers in case no
- * security model is loaded.
- *
- * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
- * Copyright (C) 2001-2002  Greg Kroah-Hartman <greg@kroah.com>
- * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.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.
- */
-
-#undef DEBUG
-
-#include <linux/capability.h>
-#include <linux/kernel.h>
-#include <linux/mman.h>
-#include <linux/pagemap.h>
-#include <linux/swap.h>
-#include <linux/security.h>
-#include <linux/skbuff.h>
-#include <linux/netlink.h>
-#include <net/sock.h>
-#include <linux/xattr.h>
-#include <linux/hugetlb.h>
-#include <linux/ptrace.h>
-#include <linux/file.h>
-#include <linux/prctl.h>
-#include <linux/securebits.h>
-
-static int dummy_ptrace (struct task_struct *parent, struct task_struct *child)
-{
-	return 0;
-}
-
-static int dummy_capget (struct task_struct *target, kernel_cap_t * effective,
-			 kernel_cap_t * inheritable, kernel_cap_t * permitted)
-{
-	if (target->euid == 0) {
-		cap_set_full(*permitted);
-		cap_set_init_eff(*effective);
-	} else {
-		cap_clear(*permitted);
-		cap_clear(*effective);
-	}
-
-	cap_clear(*inheritable);
-
-	if (target->fsuid != 0) {
-		*permitted = cap_drop_fs_set(*permitted);
-		*effective = cap_drop_fs_set(*effective);
-	}
-	return 0;
-}
-
-static int dummy_capset_check (struct task_struct *target,
-			       kernel_cap_t * effective,
-			       kernel_cap_t * inheritable,
-			       kernel_cap_t * permitted)
-{
-	return -EPERM;
-}
-
-static void dummy_capset_set (struct task_struct *target,
-			      kernel_cap_t * effective,
-			      kernel_cap_t * inheritable,
-			      kernel_cap_t * permitted)
-{
-	return;
-}
-
-static int dummy_acct (struct file *file)
-{
-	return 0;
-}
-
-static int dummy_capable (struct task_struct *tsk, int cap)
-{
-	if (cap_raised (tsk->cap_effective, cap))
-		return 0;
-	return -EPERM;
-}
-
-static int dummy_sysctl (ctl_table * table, int op)
-{
-	return 0;
-}
-
-static int dummy_quotactl (int cmds, int type, int id, struct super_block *sb)
-{
-	return 0;
-}
-
-static int dummy_quota_on (struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_syslog (int type)
-{
-	if ((type != 3 && type != 10) && current->euid)
-		return -EPERM;
-	return 0;
-}
-
-static int dummy_settime(struct timespec *ts, struct timezone *tz)
-{
-	if (!capable(CAP_SYS_TIME))
-		return -EPERM;
-	return 0;
-}
-
-static int dummy_vm_enough_memory(struct mm_struct *mm, long pages)
-{
-	int cap_sys_admin = 0;
-
-	if (dummy_capable(current, CAP_SYS_ADMIN) == 0)
-		cap_sys_admin = 1;
-	return __vm_enough_memory(mm, pages, cap_sys_admin);
-}
-
-static int dummy_bprm_alloc_security (struct linux_binprm *bprm)
-{
-	return 0;
-}
-
-static void dummy_bprm_free_security (struct linux_binprm *bprm)
-{
-	return;
-}
-
-static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
-{
-	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
-		set_dumpable(current->mm, suid_dumpable);
-
-		if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) {
-			bprm->e_uid = current->uid;
-			bprm->e_gid = current->gid;
-		}
-	}
-
-	current->suid = current->euid = current->fsuid = bprm->e_uid;
-	current->sgid = current->egid = current->fsgid = bprm->e_gid;
-
-	dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
-}
-
-static void dummy_bprm_post_apply_creds (struct linux_binprm *bprm)
-{
-	return;
-}
-
-static int dummy_bprm_set_security (struct linux_binprm *bprm)
-{
-	return 0;
-}
-
-static int dummy_bprm_check_security (struct linux_binprm *bprm)
-{
-	return 0;
-}
-
-static int dummy_bprm_secureexec (struct linux_binprm *bprm)
-{
-	/* The new userland will simply use the value provided
-	   in the AT_SECURE field to decide whether secure mode
-	   is required.  Hence, this logic is required to preserve
-	   the legacy decision algorithm used by the old userland. */
-	return (current->euid != current->uid ||
-		current->egid != current->gid);
-}
-
-static int dummy_sb_alloc_security (struct super_block *sb)
-{
-	return 0;
-}
-
-static void dummy_sb_free_security (struct super_block *sb)
-{
-	return;
-}
-
-static int dummy_sb_copy_data (char *orig, char *copy)
-{
-	return 0;
-}
-
-static int dummy_sb_kern_mount (struct super_block *sb, void *data)
-{
-	return 0;
-}
-
-static int dummy_sb_statfs (struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_sb_mount (char *dev_name, struct path *path, char *type,
-			   unsigned long flags, void *data)
-{
-	return 0;
-}
-
-static int dummy_sb_check_sb (struct vfsmount *mnt, struct path *path)
-{
-	return 0;
-}
-
-static int dummy_sb_umount (struct vfsmount *mnt, int flags)
-{
-	return 0;
-}
-
-static void dummy_sb_umount_close (struct vfsmount *mnt)
-{
-	return;
-}
-
-static void dummy_sb_umount_busy (struct vfsmount *mnt)
-{
-	return;
-}
-
-static void dummy_sb_post_remount (struct vfsmount *mnt, unsigned long flags,
-				   void *data)
-{
-	return;
-}
-
-
-static void dummy_sb_post_addmount (struct vfsmount *mnt, struct path *path)
-{
-	return;
-}
-
-static int dummy_sb_pivotroot (struct path *old_path, struct path *new_path)
-{
-	return 0;
-}
-
-static void dummy_sb_post_pivotroot (struct path *old_path, struct path *new_path)
-{
-	return;
-}
-
-static int dummy_sb_get_mnt_opts(const struct super_block *sb,
-				 struct security_mnt_opts *opts)
-{
-	security_init_mnt_opts(opts);
-	return 0;
-}
-
-static int dummy_sb_set_mnt_opts(struct super_block *sb,
-				 struct security_mnt_opts *opts)
-{
-	if (unlikely(opts->num_mnt_opts))
-		return -EOPNOTSUPP;
-	return 0;
-}
-
-static void dummy_sb_clone_mnt_opts(const struct super_block *oldsb,
-				    struct super_block *newsb)
-{
-	return;
-}
-
-static int dummy_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
-{
-	return 0;
-}
-
-static int dummy_inode_alloc_security (struct inode *inode)
-{
-	return 0;
-}
-
-static void dummy_inode_free_security (struct inode *inode)
-{
-	return;
-}
-
-static int dummy_inode_init_security (struct inode *inode, struct inode *dir,
-				      char **name, void **value, size_t *len)
-{
-	return -EOPNOTSUPP;
-}
-
-static int dummy_inode_create (struct inode *inode, struct dentry *dentry,
-			       int mask)
-{
-	return 0;
-}
-
-static int dummy_inode_link (struct dentry *old_dentry, struct inode *inode,
-			     struct dentry *new_dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_symlink (struct inode *inode, struct dentry *dentry,
-				const char *name)
-{
-	return 0;
-}
-
-static int dummy_inode_mkdir (struct inode *inode, struct dentry *dentry,
-			      int mask)
-{
-	return 0;
-}
-
-static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_mknod (struct inode *inode, struct dentry *dentry,
-			      int mode, dev_t dev)
-{
-	return 0;
-}
-
-static int dummy_inode_rename (struct inode *old_inode,
-			       struct dentry *old_dentry,
-			       struct inode *new_inode,
-			       struct dentry *new_dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_readlink (struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_follow_link (struct dentry *dentry,
-				    struct nameidata *nameidata)
-{
-	return 0;
-}
-
-static int dummy_inode_permission (struct inode *inode, int mask, struct nameidata *nd)
-{
-	return 0;
-}
-
-static int dummy_inode_setattr (struct dentry *dentry, struct iattr *iattr)
-{
-	return 0;
-}
-
-static int dummy_inode_getattr (struct vfsmount *mnt, struct dentry *dentry)
-{
-	return 0;
-}
-
-static void dummy_inode_delete (struct inode *ino)
-{
-	return;
-}
-
-static int dummy_inode_setxattr (struct dentry *dentry, const char *name,
-				 const void *value, size_t size, int flags)
-{
-	if (!strncmp(name, XATTR_SECURITY_PREFIX,
-		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
-	    !capable(CAP_SYS_ADMIN))
-		return -EPERM;
-	return 0;
-}
-
-static void dummy_inode_post_setxattr (struct dentry *dentry, const char *name,
-				       const void *value, size_t size,
-				       int flags)
-{
-}
-
-static int dummy_inode_getxattr (struct dentry *dentry, const char *name)
-{
-	return 0;
-}
-
-static int dummy_inode_listxattr (struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_removexattr (struct dentry *dentry, const char *name)
-{
-	if (!strncmp(name, XATTR_SECURITY_PREFIX,
-		     sizeof(XATTR_SECURITY_PREFIX) - 1) &&
-	    !capable(CAP_SYS_ADMIN))
-		return -EPERM;
-	return 0;
-}
-
-static int dummy_inode_need_killpriv(struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_killpriv(struct dentry *dentry)
-{
-	return 0;
-}
-
-static int dummy_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
-{
-	return -EOPNOTSUPP;
-}
-
-static int dummy_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
-{
-	return -EOPNOTSUPP;
-}
-
-static int dummy_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
-{
-	return 0;
-}
-
-static void dummy_inode_getsecid(const struct inode *inode, u32 *secid)
-{
-	*secid = 0;
-}
-
-static int dummy_file_permission (struct file *file, int mask)
-{
-	return 0;
-}
-
-static int dummy_file_alloc_security (struct file *file)
-{
-	return 0;
-}
-
-static void dummy_file_free_security (struct file *file)
-{
-	return;
-}
-
-static int dummy_file_ioctl (struct file *file, unsigned int command,
-			     unsigned long arg)
-{
-	return 0;
-}
-
-static int dummy_file_mmap (struct file *file, unsigned long reqprot,
-			    unsigned long prot,
-			    unsigned long flags,
-			    unsigned long addr,
-			    unsigned long addr_only)
-{
-	if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
-		return -EACCES;
-	return 0;
-}
-
-static int dummy_file_mprotect (struct vm_area_struct *vma,
-				unsigned long reqprot,
-				unsigned long prot)
-{
-	return 0;
-}
-
-static int dummy_file_lock (struct file *file, unsigned int cmd)
-{
-	return 0;
-}
-
-static int dummy_file_fcntl (struct file *file, unsigned int cmd,
-			     unsigned long arg)
-{
-	return 0;
-}
-
-static int dummy_file_set_fowner (struct file *file)
-{
-	return 0;
-}
-
-static int dummy_file_send_sigiotask (struct task_struct *tsk,
-				      struct fown_struct *fown, int sig)
-{
-	return 0;
-}
-
-static int dummy_file_receive (struct file *file)
-{
-	return 0;
-}
-
-static int dummy_dentry_open (struct file *file)
-{
-	return 0;
-}
-
-static int dummy_task_create (unsigned long clone_flags)
-{
-	return 0;
-}
-
-static int dummy_task_alloc_security (struct task_struct *p)
-{
-	return 0;
-}
-
-static void dummy_task_free_security (struct task_struct *p)
-{
-	return;
-}
-
-static int dummy_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
-{
-	return 0;
-}
-
-static int dummy_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
-{
-	dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
-	return 0;
-}
-
-static int dummy_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags)
-{
-	return 0;
-}
-
-static int dummy_task_setpgid (struct task_struct *p, pid_t pgid)
-{
-	return 0;
-}
-
-static int dummy_task_getpgid (struct task_struct *p)
-{
-	return 0;
-}
-
-static int dummy_task_getsid (struct task_struct *p)
-{
-	return 0;
-}
-
-static void dummy_task_getsecid (struct task_struct *p, u32 *secid)
-{
-	*secid = 0;
-}
-
-static int dummy_task_setgroups (struct group_info *group_info)
-{
-	return 0;
-}
-
-static int dummy_task_setnice (struct task_struct *p, int nice)
-{
-	return 0;
-}
-
-static int dummy_task_setioprio (struct task_struct *p, int ioprio)
-{
-	return 0;
-}
-
-static int dummy_task_getioprio (struct task_struct *p)
-{
-	return 0;
-}
-
-static int dummy_task_setrlimit (unsigned int resource, struct rlimit *new_rlim)
-{
-	return 0;
-}
-
-static int dummy_task_setscheduler (struct task_struct *p, int policy,
-				    struct sched_param *lp)
-{
-	return 0;
-}
-
-static int dummy_task_getscheduler (struct task_struct *p)
-{
-	return 0;
-}
-
-static int dummy_task_movememory (struct task_struct *p)
-{
-	return 0;
-}
-
-static int dummy_task_wait (struct task_struct *p)
-{
-	return 0;
-}
-
-static int dummy_task_kill (struct task_struct *p, struct siginfo *info,
-			    int sig, u32 secid)
-{
-	return 0;
-}
-
-static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3,
-			     unsigned long arg4, unsigned long arg5, long *rc_p)
-{
-	switch (option) {
-	case PR_CAPBSET_READ:
-		*rc_p = (cap_valid(arg2) ? 1 : -EINVAL);
-		break;
-	case PR_GET_KEEPCAPS:
-		*rc_p = issecure(SECURE_KEEP_CAPS);
-		break;
-	case PR_SET_KEEPCAPS:
-		if (arg2 > 1)
-			*rc_p = -EINVAL;
-		else if (arg2)
-			current->securebits |= issecure_mask(SECURE_KEEP_CAPS);
-		else
-			current->securebits &=
-				~issecure_mask(SECURE_KEEP_CAPS);
-		break;
-	default:
-		return 0;
-	}
-
-	return 1;
-}
-
-static void dummy_task_reparent_to_init (struct task_struct *p)
-{
-	p->euid = p->fsuid = 0;
-	return;
-}
-
-static void dummy_task_to_inode(struct task_struct *p, struct inode *inode)
-{ }
-
-static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag)
-{
-	return 0;
-}
-
-static void dummy_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
-{
-	*secid = 0;
-}
-
-static int dummy_msg_msg_alloc_security (struct msg_msg *msg)
-{
-	return 0;
-}
-
-static void dummy_msg_msg_free_security (struct msg_msg *msg)
-{
-	return;
-}
-
-static int dummy_msg_queue_alloc_security (struct msg_queue *msq)
-{
-	return 0;
-}
-
-static void dummy_msg_queue_free_security (struct msg_queue *msq)
-{
-	return;
-}
-
-static int dummy_msg_queue_associate (struct msg_queue *msq, 
-				      int msqflg)
-{
-	return 0;
-}
-
-static int dummy_msg_queue_msgctl (struct msg_queue *msq, int cmd)
-{
-	return 0;
-}
-
-static int dummy_msg_queue_msgsnd (struct msg_queue *msq, struct msg_msg *msg,
-				   int msgflg)
-{
-	return 0;
-}
-
-static int dummy_msg_queue_msgrcv (struct msg_queue *msq, struct msg_msg *msg,
-				   struct task_struct *target, long type,
-				   int mode)
-{
-	return 0;
-}
-
-static int dummy_shm_alloc_security (struct shmid_kernel *shp)
-{
-	return 0;
-}
-
-static void dummy_shm_free_security (struct shmid_kernel *shp)
-{
-	return;
-}
-
-static int dummy_shm_associate (struct shmid_kernel *shp, int shmflg)
-{
-	return 0;
-}
-
-static int dummy_shm_shmctl (struct shmid_kernel *shp, int cmd)
-{
-	return 0;
-}
-
-static int dummy_shm_shmat (struct shmid_kernel *shp, char __user *shmaddr,
-			    int shmflg)
-{
-	return 0;
-}
-
-static int dummy_sem_alloc_security (struct sem_array *sma)
-{
-	return 0;
-}
-
-static void dummy_sem_free_security (struct sem_array *sma)
-{
-	return;
-}
-
-static int dummy_sem_associate (struct sem_array *sma, int semflg)
-{
-	return 0;
-}
-
-static int dummy_sem_semctl (struct sem_array *sma, int cmd)
-{
-	return 0;
-}
-
-static int dummy_sem_semop (struct sem_array *sma, 
-			    struct sembuf *sops, unsigned nsops, int alter)
-{
-	return 0;
-}
-
-static int dummy_netlink_send (struct sock *sk, struct sk_buff *skb)
-{
-	NETLINK_CB(skb).eff_cap = current->cap_effective;
-	return 0;
-}
-
-static int dummy_netlink_recv (struct sk_buff *skb, int cap)
-{
-	if (!cap_raised (NETLINK_CB (skb).eff_cap, cap))
-		return -EPERM;
-	return 0;
-}
-
-#ifdef CONFIG_SECURITY_NETWORK
-static int dummy_unix_stream_connect (struct socket *sock,
-				      struct socket *other,
-				      struct sock *newsk)
-{
-	return 0;
-}
-
-static int dummy_unix_may_send (struct socket *sock,
-				struct socket *other)
-{
-	return 0;
-}
-
-static int dummy_socket_create (int family, int type,
-				int protocol, int kern)
-{
-	return 0;
-}
-
-static int dummy_socket_post_create (struct socket *sock, int family, int type,
-				     int protocol, int kern)
-{
-	return 0;
-}
-
-static int dummy_socket_bind (struct socket *sock, struct sockaddr *address,
-			      int addrlen)
-{
-	return 0;
-}
-
-static int dummy_socket_connect (struct socket *sock, struct sockaddr *address,
-				 int addrlen)
-{
-	return 0;
-}
-
-static int dummy_socket_listen (struct socket *sock, int backlog)
-{
-	return 0;
-}
-
-static int dummy_socket_accept (struct socket *sock, struct socket *newsock)
-{
-	return 0;
-}
-
-static void dummy_socket_post_accept (struct socket *sock, 
-				      struct socket *newsock)
-{
-	return;
-}
-
-static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg,
-				 int size)
-{
-	return 0;
-}
-
-static int dummy_socket_recvmsg (struct socket *sock, struct msghdr *msg,
-				 int size, int flags)
-{
-	return 0;
-}
-
-static int dummy_socket_getsockname (struct socket *sock)
-{
-	return 0;
-}
-
-static int dummy_socket_getpeername (struct socket *sock)
-{
-	return 0;
-}
-
-static int dummy_socket_setsockopt (struct socket *sock, int level, int optname)
-{
-	return 0;
-}
-
-static int dummy_socket_getsockopt (struct socket *sock, int level, int optname)
-{
-	return 0;
-}
-
-static int dummy_socket_shutdown (struct socket *sock, int how)
-{
-	return 0;
-}
-
-static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)
-{
-	return 0;
-}
-
-static int dummy_socket_getpeersec_stream(struct socket *sock, char __user *optval,
-					  int __user *optlen, unsigned len)
-{
-	return -ENOPROTOOPT;
-}
-
-static int dummy_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
-{
-	return -ENOPROTOOPT;
-}
-
-static inline int dummy_sk_alloc_security (struct sock *sk, int family, gfp_t priority)
-{
-	return 0;
-}
-
-static inline void dummy_sk_free_security (struct sock *sk)
-{
-}
-
-static inline void dummy_sk_clone_security (const struct sock *sk, struct sock *newsk)
-{
-}
-
-static inline void dummy_sk_getsecid(struct sock *sk, u32 *secid)
-{
-}
-
-static inline void dummy_sock_graft(struct sock* sk, struct socket *parent)
-{
-}
-
-static inline int dummy_inet_conn_request(struct sock *sk,
-			struct sk_buff *skb, struct request_sock *req)
-{
-	return 0;
-}
-
-static inline void dummy_inet_csk_clone(struct sock *newsk,
-			const struct request_sock *req)
-{
-}
-
-static inline void dummy_inet_conn_established(struct sock *sk,
-			struct sk_buff *skb)
-{
-}
-
-static inline void dummy_req_classify_flow(const struct request_sock *req,
-			struct flowi *fl)
-{
-}
-#endif	/* CONFIG_SECURITY_NETWORK */
-
-#ifdef CONFIG_SECURITY_NETWORK_XFRM
-static int dummy_xfrm_policy_alloc_security(struct xfrm_sec_ctx **ctxp,
-					    struct xfrm_user_sec_ctx *sec_ctx)
-{
-	return 0;
-}
-
-static inline int dummy_xfrm_policy_clone_security(struct xfrm_sec_ctx *old_ctx,
-					   struct xfrm_sec_ctx **new_ctxp)
-{
-	return 0;
-}
-
-static void dummy_xfrm_policy_free_security(struct xfrm_sec_ctx *ctx)
-{
-}
-
-static int dummy_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)
-{
-	return 0;
-}
-
-static int dummy_xfrm_state_alloc_security(struct xfrm_state *x,
-	struct xfrm_user_sec_ctx *sec_ctx, u32 secid)
-{
-	return 0;
-}
-
-static void dummy_xfrm_state_free_security(struct xfrm_state *x)
-{
-}
-
-static int dummy_xfrm_state_delete_security(struct xfrm_state *x)
-{
-	return 0;
-}
-
-static int dummy_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx,
-				    u32 sk_sid, u8 dir)
-{
-	return 0;
-}
-
-static int dummy_xfrm_state_pol_flow_match(struct xfrm_state *x,
-				struct xfrm_policy *xp, struct flowi *fl)
-{
-	return 1;
-}
-
-static int dummy_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall)
-{
-	return 0;
-}
-
-#endif /* CONFIG_SECURITY_NETWORK_XFRM */
-static int dummy_register_security (const char *name, struct security_operations *ops)
-{
-	return -EINVAL;
-}
-
-static void dummy_d_instantiate (struct dentry *dentry, struct inode *inode)
-{
-	return;
-}
-
-static int dummy_getprocattr(struct task_struct *p, char *name, char **value)
-{
-	return -EINVAL;
-}
-
-static int dummy_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
-{
-	return -EINVAL;
-}
-
-static int dummy_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
-{
-	return -EOPNOTSUPP;
-}
-
-static int dummy_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
-{
-	return -EOPNOTSUPP;
-}
-
-static void dummy_release_secctx(char *secdata, u32 seclen)
-{
-}
-
-#ifdef CONFIG_KEYS
-static inline int dummy_key_alloc(struct key *key, struct task_struct *ctx,
-				  unsigned long flags)
-{
-	return 0;
-}
-
-static inline void dummy_key_free(struct key *key)
-{
-}
-
-static inline int dummy_key_permission(key_ref_t key_ref,
-				       struct task_struct *context,
-				       key_perm_t perm)
-{
-	return 0;
-}
-
-static int dummy_key_getsecurity(struct key *key, char **_buffer)
-{
-	*_buffer = NULL;
-	return 0;
-}
-
-#endif /* CONFIG_KEYS */
-
-#ifdef CONFIG_AUDIT
-static inline int dummy_audit_rule_init(u32 field, u32 op, char *rulestr,
-					void **lsmrule)
-{
-	return 0;
-}
-
-static inline int dummy_audit_rule_known(struct audit_krule *krule)
-{
-	return 0;
-}
-
-static inline int dummy_audit_rule_match(u32 secid, u32 field, u32 op,
-					 void *lsmrule,
-					 struct audit_context *actx)
-{
-	return 0;
-}
-
-static inline void dummy_audit_rule_free(void *lsmrule)
-{ }
-
-#endif /* CONFIG_AUDIT */
-
-struct security_operations dummy_security_ops = {
-	.name = "dummy",
-};
-
-#define set_to_dummy_if_null(ops, function)				\
-	do {								\
-		if (!ops->function) {					\
-			ops->function = dummy_##function;		\
-			pr_debug("Had to override the " #function	\
-				 " security operation with the dummy one.\n");\
-			}						\
-	} while (0)
-
-void security_fixup_ops (struct security_operations *ops)
-{
-	set_to_dummy_if_null(ops, ptrace);
-	set_to_dummy_if_null(ops, capget);
-	set_to_dummy_if_null(ops, capset_check);
-	set_to_dummy_if_null(ops, capset_set);
-	set_to_dummy_if_null(ops, acct);
-	set_to_dummy_if_null(ops, capable);
-	set_to_dummy_if_null(ops, quotactl);
-	set_to_dummy_if_null(ops, quota_on);
-	set_to_dummy_if_null(ops, sysctl);
-	set_to_dummy_if_null(ops, syslog);
-	set_to_dummy_if_null(ops, settime);
-	set_to_dummy_if_null(ops, vm_enough_memory);
-	set_to_dummy_if_null(ops, bprm_alloc_security);
-	set_to_dummy_if_null(ops, bprm_free_security);
-	set_to_dummy_if_null(ops, bprm_apply_creds);
-	set_to_dummy_if_null(ops, bprm_post_apply_creds);
-	set_to_dummy_if_null(ops, bprm_set_security);
-	set_to_dummy_if_null(ops, bprm_check_security);
-	set_to_dummy_if_null(ops, bprm_secureexec);
-	set_to_dummy_if_null(ops, sb_alloc_security);
-	set_to_dummy_if_null(ops, sb_free_security);
-	set_to_dummy_if_null(ops, sb_copy_data);
-	set_to_dummy_if_null(ops, sb_kern_mount);
-	set_to_dummy_if_null(ops, sb_statfs);
-	set_to_dummy_if_null(ops, sb_mount);
-	set_to_dummy_if_null(ops, sb_check_sb);
-	set_to_dummy_if_null(ops, sb_umount);
-	set_to_dummy_if_null(ops, sb_umount_close);
-	set_to_dummy_if_null(ops, sb_umount_busy);
-	set_to_dummy_if_null(ops, sb_post_remount);
-	set_to_dummy_if_null(ops, sb_post_addmount);
-	set_to_dummy_if_null(ops, sb_pivotroot);
-	set_to_dummy_if_null(ops, sb_post_pivotroot);
-	set_to_dummy_if_null(ops, sb_get_mnt_opts);
-	set_to_dummy_if_null(ops, sb_set_mnt_opts);
-	set_to_dummy_if_null(ops, sb_clone_mnt_opts);
-	set_to_dummy_if_null(ops, sb_parse_opts_str);
-	set_to_dummy_if_null(ops, inode_alloc_security);
-	set_to_dummy_if_null(ops, inode_free_security);
-	set_to_dummy_if_null(ops, inode_init_security);
-	set_to_dummy_if_null(ops, inode_create);
-	set_to_dummy_if_null(ops, inode_link);
-	set_to_dummy_if_null(ops, inode_unlink);
-	set_to_dummy_if_null(ops, inode_symlink);
-	set_to_dummy_if_null(ops, inode_mkdir);
-	set_to_dummy_if_null(ops, inode_rmdir);
-	set_to_dummy_if_null(ops, inode_mknod);
-	set_to_dummy_if_null(ops, inode_rename);
-	set_to_dummy_if_null(ops, inode_readlink);
-	set_to_dummy_if_null(ops, inode_follow_link);
-	set_to_dummy_if_null(ops, inode_permission);
-	set_to_dummy_if_null(ops, inode_setattr);
-	set_to_dummy_if_null(ops, inode_getattr);
-	set_to_dummy_if_null(ops, inode_delete);
-	set_to_dummy_if_null(ops, inode_setxattr);
-	set_to_dummy_if_null(ops, inode_post_setxattr);
-	set_to_dummy_if_null(ops, inode_getxattr);
-	set_to_dummy_if_null(ops, inode_listxattr);
-	set_to_dummy_if_null(ops, inode_removexattr);
-	set_to_dummy_if_null(ops, inode_need_killpriv);
-	set_to_dummy_if_null(ops, inode_killpriv);
-	set_to_dummy_if_null(ops, inode_getsecurity);
-	set_to_dummy_if_null(ops, inode_setsecurity);
-	set_to_dummy_if_null(ops, inode_listsecurity);
-	set_to_dummy_if_null(ops, inode_getsecid);
-	set_to_dummy_if_null(ops, file_permission);
-	set_to_dummy_if_null(ops, file_alloc_security);
-	set_to_dummy_if_null(ops, file_free_security);
-	set_to_dummy_if_null(ops, file_ioctl);
-	set_to_dummy_if_null(ops, file_mmap);
-	set_to_dummy_if_null(ops, file_mprotect);
-	set_to_dummy_if_null(ops, file_lock);
-	set_to_dummy_if_null(ops, file_fcntl);
-	set_to_dummy_if_null(ops, file_set_fowner);
-	set_to_dummy_if_null(ops, file_send_sigiotask);
-	set_to_dummy_if_null(ops, file_receive);
-	set_to_dummy_if_null(ops, dentry_open);
-	set_to_dummy_if_null(ops, task_create);
-	set_to_dummy_if_null(ops, task_alloc_security);
-	set_to_dummy_if_null(ops, task_free_security);
-	set_to_dummy_if_null(ops, task_setuid);
-	set_to_dummy_if_null(ops, task_post_setuid);
-	set_to_dummy_if_null(ops, task_setgid);
-	set_to_dummy_if_null(ops, task_setpgid);
-	set_to_dummy_if_null(ops, task_getpgid);
-	set_to_dummy_if_null(ops, task_getsid);
-	set_to_dummy_if_null(ops, task_getsecid);
-	set_to_dummy_if_null(ops, task_setgroups);
-	set_to_dummy_if_null(ops, task_setnice);
-	set_to_dummy_if_null(ops, task_setioprio);
-	set_to_dummy_if_null(ops, task_getioprio);
-	set_to_dummy_if_null(ops, task_setrlimit);
-	set_to_dummy_if_null(ops, task_setscheduler);
-	set_to_dummy_if_null(ops, task_getscheduler);
-	set_to_dummy_if_null(ops, task_movememory);
-	set_to_dummy_if_null(ops, task_wait);
-	set_to_dummy_if_null(ops, task_kill);
-	set_to_dummy_if_null(ops, task_prctl);
-	set_to_dummy_if_null(ops, task_reparent_to_init);
- 	set_to_dummy_if_null(ops, task_to_inode);
-	set_to_dummy_if_null(ops, ipc_permission);
-	set_to_dummy_if_null(ops, ipc_getsecid);
-	set_to_dummy_if_null(ops, msg_msg_alloc_security);
-	set_to_dummy_if_null(ops, msg_msg_free_security);
-	set_to_dummy_if_null(ops, msg_queue_alloc_security);
-	set_to_dummy_if_null(ops, msg_queue_free_security);
-	set_to_dummy_if_null(ops, msg_queue_associate);
-	set_to_dummy_if_null(ops, msg_queue_msgctl);
-	set_to_dummy_if_null(ops, msg_queue_msgsnd);
-	set_to_dummy_if_null(ops, msg_queue_msgrcv);
-	set_to_dummy_if_null(ops, shm_alloc_security);
-	set_to_dummy_if_null(ops, shm_free_security);
-	set_to_dummy_if_null(ops, shm_associate);
-	set_to_dummy_if_null(ops, shm_shmctl);
-	set_to_dummy_if_null(ops, shm_shmat);
-	set_to_dummy_if_null(ops, sem_alloc_security);
-	set_to_dummy_if_null(ops, sem_free_security);
-	set_to_dummy_if_null(ops, sem_associate);
-	set_to_dummy_if_null(ops, sem_semctl);
-	set_to_dummy_if_null(ops, sem_semop);
-	set_to_dummy_if_null(ops, netlink_send);
-	set_to_dummy_if_null(ops, netlink_recv);
-	set_to_dummy_if_null(ops, register_security);
-	set_to_dummy_if_null(ops, d_instantiate);
- 	set_to_dummy_if_null(ops, getprocattr);
- 	set_to_dummy_if_null(ops, setprocattr);
- 	set_to_dummy_if_null(ops, secid_to_secctx);
-	set_to_dummy_if_null(ops, secctx_to_secid);
- 	set_to_dummy_if_null(ops, release_secctx);
-#ifdef CONFIG_SECURITY_NETWORK
-	set_to_dummy_if_null(ops, unix_stream_connect);
-	set_to_dummy_if_null(ops, unix_may_send);
-	set_to_dummy_if_null(ops, socket_create);
-	set_to_dummy_if_null(ops, socket_post_create);
-	set_to_dummy_if_null(ops, socket_bind);
-	set_to_dummy_if_null(ops, socket_connect);
-	set_to_dummy_if_null(ops, socket_listen);
-	set_to_dummy_if_null(ops, socket_accept);
-	set_to_dummy_if_null(ops, socket_post_accept);
-	set_to_dummy_if_null(ops, socket_sendmsg);
-	set_to_dummy_if_null(ops, socket_recvmsg);
-	set_to_dummy_if_null(ops, socket_getsockname);
-	set_to_dummy_if_null(ops, socket_getpeername);
-	set_to_dummy_if_null(ops, socket_setsockopt);
-	set_to_dummy_if_null(ops, socket_getsockopt);
-	set_to_dummy_if_null(ops, socket_shutdown);
-	set_to_dummy_if_null(ops, socket_sock_rcv_skb);
-	set_to_dummy_if_null(ops, socket_getpeersec_stream);
-	set_to_dummy_if_null(ops, socket_getpeersec_dgram);
-	set_to_dummy_if_null(ops, sk_alloc_security);
-	set_to_dummy_if_null(ops, sk_free_security);
-	set_to_dummy_if_null(ops, sk_clone_security);
-	set_to_dummy_if_null(ops, sk_getsecid);
-	set_to_dummy_if_null(ops, sock_graft);
-	set_to_dummy_if_null(ops, inet_conn_request);
-	set_to_dummy_if_null(ops, inet_csk_clone);
-	set_to_dummy_if_null(ops, inet_conn_established);
-	set_to_dummy_if_null(ops, req_classify_flow);
- #endif	/* CONFIG_SECURITY_NETWORK */
-#ifdef  CONFIG_SECURITY_NETWORK_XFRM
-	set_to_dummy_if_null(ops, xfrm_policy_alloc_security);
-	set_to_dummy_if_null(ops, xfrm_policy_clone_security);
-	set_to_dummy_if_null(ops, xfrm_policy_free_security);
-	set_to_dummy_if_null(ops, xfrm_policy_delete_security);
-	set_to_dummy_if_null(ops, xfrm_state_alloc_security);
-	set_to_dummy_if_null(ops, xfrm_state_free_security);
-	set_to_dummy_if_null(ops, xfrm_state_delete_security);
-	set_to_dummy_if_null(ops, xfrm_policy_lookup);
-	set_to_dummy_if_null(ops, xfrm_state_pol_flow_match);
-	set_to_dummy_if_null(ops, xfrm_decode_session);
-#endif	/* CONFIG_SECURITY_NETWORK_XFRM */
-#ifdef CONFIG_KEYS
-	set_to_dummy_if_null(ops, key_alloc);
-	set_to_dummy_if_null(ops, key_free);
-	set_to_dummy_if_null(ops, key_permission);
-	set_to_dummy_if_null(ops, key_getsecurity);
-#endif	/* CONFIG_KEYS */
-#ifdef CONFIG_AUDIT
-	set_to_dummy_if_null(ops, audit_rule_init);
-	set_to_dummy_if_null(ops, audit_rule_known);
-	set_to_dummy_if_null(ops, audit_rule_match);
-	set_to_dummy_if_null(ops, audit_rule_free);
-#endif
-}
-
diff --git a/security/root_plug.c b/security/root_plug.c
index a41cf42..be0ebec 100644
--- a/security/root_plug.c
+++ b/security/root_plug.c
@@ -28,9 +28,6 @@
 #include <linux/usb.h>
 #include <linux/moduleparam.h>
 
-/* flag to keep track of how we were registered */
-static int secondary;
-
 /* default is a generic type of usb to serial converter */
 static int vendor_id = 0x0557;
 static int product_id = 0x2008;
@@ -97,13 +94,7 @@
 	if (register_security (&rootplug_security_ops)) {
 		printk (KERN_INFO 
 			"Failure registering Root Plug module with the kernel\n");
-		/* try registering with primary module */
-		if (mod_reg_security (MY_NAME, &rootplug_security_ops)) {
-			printk (KERN_INFO "Failure registering Root Plug "
-				" module with primary security module.\n");
 			return -EINVAL;
-		}
-		secondary = 1;
 	}
 	printk (KERN_INFO "Root Plug module initialized, "
 		"vendor_id = %4.4x, product id = %4.4x\n", vendor_id, product_id);
diff --git a/security/security.c b/security/security.c
index 59838a9..59f23b5 100644
--- a/security/security.c
+++ b/security/security.c
@@ -20,8 +20,8 @@
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1];
 
-/* things that live in dummy.c */
-extern struct security_operations dummy_security_ops;
+/* things that live in capability.c */
+extern struct security_operations default_security_ops;
 extern void security_fixup_ops(struct security_operations *ops);
 
 struct security_operations *security_ops;	/* Initialized to NULL */
@@ -57,13 +57,8 @@
 {
 	printk(KERN_INFO "Security Framework initialized\n");
 
-	if (verify(&dummy_security_ops)) {
-		printk(KERN_ERR "%s could not verify "
-		       "dummy_security_ops structure.\n", __func__);
-		return -EIO;
-	}
-
-	security_ops = &dummy_security_ops;
+	security_fixup_ops(&default_security_ops);
+	security_ops = &default_security_ops;
 	do_security_initcalls();
 
 	return 0;
@@ -122,7 +117,7 @@
 		return -EINVAL;
 	}
 
-	if (security_ops != &dummy_security_ops)
+	if (security_ops != &default_security_ops)
 		return -EAGAIN;
 
 	security_ops = ops;
@@ -130,40 +125,12 @@
 	return 0;
 }
 
-/**
- * mod_reg_security - allows security modules to be "stacked"
- * @name: a pointer to a string with the name of the security_options to be registered
- * @ops: a pointer to the struct security_options that is to be registered
- *
- * This function allows security modules to be stacked if the currently loaded
- * security module allows this to happen.  It passes the @name and @ops to the
- * register_security function of the currently loaded security module.
- *
- * The return value depends on the currently loaded security module, with 0 as
- * success.
- */
-int mod_reg_security(const char *name, struct security_operations *ops)
-{
-	if (verify(ops)) {
-		printk(KERN_INFO "%s could not verify "
-		       "security operations.\n", __func__);
-		return -EINVAL;
-	}
-
-	if (ops == security_ops) {
-		printk(KERN_INFO "%s security operations "
-		       "already registered.\n", __func__);
-		return -EINVAL;
-	}
-
-	return security_ops->register_security(name, ops);
-}
-
 /* Security operations */
 
-int security_ptrace(struct task_struct *parent, struct task_struct *child)
+int security_ptrace(struct task_struct *parent, struct task_struct *child,
+		    unsigned int mode)
 {
-	return security_ops->ptrace(parent, child);
+	return security_ops->ptrace(parent, child, mode);
 }
 
 int security_capget(struct task_struct *target,
@@ -291,6 +258,11 @@
 	return security_ops->sb_kern_mount(sb, data);
 }
 
+int security_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+	return security_ops->sb_show_options(m, sb);
+}
+
 int security_sb_statfs(struct dentry *dentry)
 {
 	return security_ops->sb_statfs(dentry);
@@ -342,12 +314,6 @@
 	security_ops->sb_post_pivotroot(old_path, new_path);
 }
 
-int security_sb_get_mnt_opts(const struct super_block *sb,
-				struct security_mnt_opts *opts)
-{
-	return security_ops->sb_get_mnt_opts(sb, opts);
-}
-
 int security_sb_set_mnt_opts(struct super_block *sb,
 				struct security_mnt_opts *opts)
 {
@@ -894,7 +860,7 @@
 
 void security_release_secctx(char *secdata, u32 seclen)
 {
-	return security_ops->release_secctx(secdata, seclen);
+	security_ops->release_secctx(secdata, seclen);
 }
 EXPORT_SYMBOL(security_release_secctx);
 
@@ -1011,12 +977,12 @@
 
 void security_sk_free(struct sock *sk)
 {
-	return security_ops->sk_free_security(sk);
+	security_ops->sk_free_security(sk);
 }
 
 void security_sk_clone(const struct sock *sk, struct sock *newsk)
 {
-	return security_ops->sk_clone_security(sk, newsk);
+	security_ops->sk_clone_security(sk, newsk);
 }
 
 void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1c864c0..91200fe 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -9,7 +9,8 @@
  *	      James Morris <jmorris@redhat.com>
  *
  *  Copyright (C) 2001,2002 Networks Associates Technology, Inc.
- *  Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *  Copyright (C) 2003-2008 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *					   Eric Paris <eparis@redhat.com>
  *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
  *			    <dgoeddel@trustedcs.com>
  *  Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
@@ -42,9 +43,7 @@
 #include <linux/fdtable.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
-#include <linux/ext2_fs.h>
 #include <linux/proc_fs.h>
-#include <linux/kd.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
 #include <linux/tty.h>
@@ -53,7 +52,7 @@
 #include <net/tcp.h>		/* struct or_callable used in sock_rcv_skb */
 #include <net/net_namespace.h>
 #include <net/netlabel.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <asm/ioctls.h>
 #include <asm/atomic.h>
 #include <linux/bitops.h>
@@ -104,7 +103,9 @@
 
 static int __init enforcing_setup(char *str)
 {
-	selinux_enforcing = simple_strtol(str, NULL, 0);
+	unsigned long enforcing;
+	if (!strict_strtoul(str, 0, &enforcing))
+		selinux_enforcing = enforcing ? 1 : 0;
 	return 1;
 }
 __setup("enforcing=", enforcing_setup);
@@ -115,7 +116,9 @@
 
 static int __init selinux_enabled_setup(char *str)
 {
-	selinux_enabled = simple_strtol(str, NULL, 0);
+	unsigned long enabled;
+	if (!strict_strtoul(str, 0, &enabled))
+		selinux_enabled = enabled ? 1 : 0;
 	return 1;
 }
 __setup("selinux=", selinux_enabled_setup);
@@ -123,13 +126,11 @@
 int selinux_enabled = 1;
 #endif
 
-/* Original (dummy) security module. */
-static struct security_operations *original_ops;
 
-/* Minimal support for a secondary security module,
-   just to allow the use of the dummy or capability modules.
-   The owlsm module can alternatively be used as a secondary
-   module as long as CONFIG_OWLSM_FD is not enabled. */
+/*
+ * Minimal support for a secondary security module,
+ * just to allow the use of the capability module.
+ */
 static struct security_operations *secondary_ops;
 
 /* Lists of inode and superblock security structures initialized
@@ -554,13 +555,15 @@
 	struct task_security_struct *tsec = current->security;
 	struct superblock_security_struct *sbsec = sb->s_security;
 	const char *name = sb->s_type->name;
-	struct inode *inode = sbsec->sb->s_root->d_inode;
-	struct inode_security_struct *root_isec = inode->i_security;
+	struct dentry *root = sb->s_root;
+	struct inode *root_inode = root->d_inode;
+	struct inode_security_struct *root_isec = root_inode->i_security;
 	u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
 	u32 defcontext_sid = 0;
 	char **mount_options = opts->mnt_opts;
 	int *flags = opts->mnt_opts_flags;
 	int num_opts = opts->num_mnt_opts;
+	bool can_xattr = false;
 
 	mutex_lock(&sbsec->lock);
 
@@ -594,7 +597,7 @@
 	 */
 	if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
 	    && (num_opts == 0))
-	        goto out;
+		goto out;
 
 	/*
 	 * parse the mount options, check if they are valid sids.
@@ -664,14 +667,24 @@
 		goto out;
 	}
 
-	if (strcmp(sb->s_type->name, "proc") == 0)
+	if (strcmp(name, "proc") == 0)
 		sbsec->proc = 1;
 
+	/*
+	 * test if the fs supports xattrs, fs_use might make use of this if the
+	 * fs has no definition in policy.
+	 */
+	if (root_inode->i_op->getxattr) {
+		rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+		if (rc >= 0 || rc == -ENODATA)
+			can_xattr = true;
+	}
+
 	/* Determine the labeling behavior to use for this filesystem type. */
-	rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+	rc = security_fs_use(name, &sbsec->behavior, &sbsec->sid, can_xattr);
 	if (rc) {
 		printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
-		       __func__, sb->s_type->name, rc);
+		       __func__, name, rc);
 		goto out;
 	}
 
@@ -956,6 +969,57 @@
 	return rc;
 }
 
+void selinux_write_opts(struct seq_file *m, struct security_mnt_opts *opts)
+{
+	int i;
+	char *prefix;
+
+	for (i = 0; i < opts->num_mnt_opts; i++) {
+		char *has_comma = strchr(opts->mnt_opts[i], ',');
+
+		switch (opts->mnt_opts_flags[i]) {
+		case CONTEXT_MNT:
+			prefix = CONTEXT_STR;
+			break;
+		case FSCONTEXT_MNT:
+			prefix = FSCONTEXT_STR;
+			break;
+		case ROOTCONTEXT_MNT:
+			prefix = ROOTCONTEXT_STR;
+			break;
+		case DEFCONTEXT_MNT:
+			prefix = DEFCONTEXT_STR;
+			break;
+		default:
+			BUG();
+		};
+		/* we need a comma before each option */
+		seq_putc(m, ',');
+		seq_puts(m, prefix);
+		if (has_comma)
+			seq_putc(m, '\"');
+		seq_puts(m, opts->mnt_opts[i]);
+		if (has_comma)
+			seq_putc(m, '\"');
+	}
+}
+
+static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+	struct security_mnt_opts opts;
+	int rc;
+
+	rc = selinux_get_mnt_opts(sb, &opts);
+	if (rc)
+		return rc;
+
+	selinux_write_opts(m, &opts);
+
+	security_free_mnt_opts(&opts);
+
+	return rc;
+}
+
 static inline u16 inode_mode_to_security_class(umode_t mode)
 {
 	switch (mode & S_IFMT) {
@@ -1682,14 +1746,23 @@
 
 /* Hook functions begin here. */
 
-static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
+static int selinux_ptrace(struct task_struct *parent,
+			  struct task_struct *child,
+			  unsigned int mode)
 {
 	int rc;
 
-	rc = secondary_ops->ptrace(parent, child);
+	rc = secondary_ops->ptrace(parent, child, mode);
 	if (rc)
 		return rc;
 
+	if (mode == PTRACE_MODE_READ) {
+		struct task_security_struct *tsec = parent->security;
+		struct task_security_struct *csec = child->security;
+		return avc_has_perm(tsec->sid, csec->sid,
+				    SECCLASS_FILE, FILE__READ, NULL);
+	}
+
 	return task_has_perm(parent, child, PROCESS__PTRACE);
 }
 
@@ -2495,7 +2568,7 @@
 	}
 
 	if (value && len) {
-		rc = security_sid_to_context(newsid, &context, &clen);
+		rc = security_sid_to_context_force(newsid, &context, &clen);
 		if (rc) {
 			kfree(namep);
 			return rc;
@@ -2669,6 +2742,11 @@
 		return rc;
 
 	rc = security_context_to_sid(value, size, &newsid);
+	if (rc == -EINVAL) {
+		if (!capable(CAP_MAC_ADMIN))
+			return rc;
+		rc = security_context_to_sid_force(value, size, &newsid);
+	}
 	if (rc)
 		return rc;
 
@@ -2690,7 +2768,7 @@
 }
 
 static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
-                                        const void *value, size_t size,
+					const void *value, size_t size,
 					int flags)
 {
 	struct inode *inode = dentry->d_inode;
@@ -2703,10 +2781,11 @@
 		return;
 	}
 
-	rc = security_context_to_sid(value, size, &newsid);
+	rc = security_context_to_sid_force(value, size, &newsid);
 	if (rc) {
-		printk(KERN_WARNING "%s:  unable to obtain SID for context "
-		       "%s, rc=%d\n", __func__, (char *)value, -rc);
+		printk(KERN_ERR "SELinux:  unable to map context to SID"
+		       "for (%s, %lu), rc=%d\n",
+		       inode->i_sb->s_id, inode->i_ino, -rc);
 		return;
 	}
 
@@ -2735,9 +2814,7 @@
 }
 
 /*
- * Copy the in-core inode security context value to the user.  If the
- * getxattr() prior to this succeeded, check to see if we need to
- * canonicalize the value to be finally returned to the user.
+ * Copy the inode security context value to the user.
  *
  * Permission check is handled by selinux_inode_getxattr hook.
  */
@@ -2746,12 +2823,33 @@
 	u32 size;
 	int error;
 	char *context = NULL;
+	struct task_security_struct *tsec = current->security;
 	struct inode_security_struct *isec = inode->i_security;
 
 	if (strcmp(name, XATTR_SELINUX_SUFFIX))
 		return -EOPNOTSUPP;
 
-	error = security_sid_to_context(isec->sid, &context, &size);
+	/*
+	 * If the caller has CAP_MAC_ADMIN, then get the raw context
+	 * value even if it is not defined by current policy; otherwise,
+	 * use the in-core value under current policy.
+	 * Use the non-auditing forms of the permission checks since
+	 * getxattr may be called by unprivileged processes commonly
+	 * and lack of permission just means that we fall back to the
+	 * in-core context value, not a denial.
+	 */
+	error = secondary_ops->capable(current, CAP_MAC_ADMIN);
+	if (!error)
+		error = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+					     SECCLASS_CAPABILITY2,
+					     CAPABILITY2__MAC_ADMIN,
+					     0,
+					     NULL);
+	if (!error)
+		error = security_sid_to_context_force(isec->sid, &context,
+						      &size);
+	else
+		error = security_sid_to_context(isec->sid, &context, &size);
 	if (error)
 		return error;
 	error = size;
@@ -2865,46 +2963,16 @@
 static int selinux_file_ioctl(struct file *file, unsigned int cmd,
 			      unsigned long arg)
 {
-	int error = 0;
+	u32 av = 0;
 
-	switch (cmd) {
-	case FIONREAD:
-	/* fall through */
-	case FIBMAP:
-	/* fall through */
-	case FIGETBSZ:
-	/* fall through */
-	case EXT2_IOC_GETFLAGS:
-	/* fall through */
-	case EXT2_IOC_GETVERSION:
-		error = file_has_perm(current, file, FILE__GETATTR);
-		break;
+	if (_IOC_DIR(cmd) & _IOC_WRITE)
+		av |= FILE__WRITE;
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		av |= FILE__READ;
+	if (!av)
+		av = FILE__IOCTL;
 
-	case EXT2_IOC_SETFLAGS:
-	/* fall through */
-	case EXT2_IOC_SETVERSION:
-		error = file_has_perm(current, file, FILE__SETATTR);
-		break;
-
-	/* sys_ioctl() checks */
-	case FIONBIO:
-	/* fall through */
-	case FIOASYNC:
-		error = file_has_perm(current, file, 0);
-		break;
-
-	case KDSKBENT:
-	case KDSKBSENT:
-		error = task_has_capability(current, CAP_SYS_TTY_CONFIG);
-		break;
-
-	/* default case assumes that the command will go
-	 * to the file's ioctl() function.
-	 */
-	default:
-		error = file_has_perm(current, file, FILE__IOCTL);
-	}
-	return error;
+	return file_has_perm(current, file, av);
 }
 
 static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
@@ -3663,7 +3731,7 @@
 		struct sockaddr_in6 *addr6 = NULL;
 		unsigned short snum;
 		struct sock *sk = sock->sk;
-		u32 sid, node_perm, addrlen;
+		u32 sid, node_perm;
 
 		tsec = current->security;
 		isec = SOCK_INODE(sock)->i_security;
@@ -3671,12 +3739,10 @@
 		if (family == PF_INET) {
 			addr4 = (struct sockaddr_in *)address;
 			snum = ntohs(addr4->sin_port);
-			addrlen = sizeof(addr4->sin_addr.s_addr);
 			addrp = (char *)&addr4->sin_addr.s_addr;
 		} else {
 			addr6 = (struct sockaddr_in6 *)address;
 			snum = ntohs(addr6->sin6_port);
-			addrlen = sizeof(addr6->sin6_addr.s6_addr);
 			addrp = (char *)&addr6->sin6_addr.s6_addr;
 		}
 
@@ -5047,24 +5113,6 @@
 	*secid = isec->sid;
 }
 
-/* module stacking operations */
-static int selinux_register_security(const char *name, struct security_operations *ops)
-{
-	if (secondary_ops != original_ops) {
-		printk(KERN_ERR "%s:  There is already a secondary security "
-		       "module registered.\n", __func__);
-		return -EINVAL;
-	}
-
-	secondary_ops = ops;
-
-	printk(KERN_INFO "%s:  Registering secondary module %s\n",
-	       __func__,
-	       name);
-
-	return 0;
-}
-
 static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
 {
 	if (inode)
@@ -5153,6 +5201,12 @@
 			size--;
 		}
 		error = security_context_to_sid(value, size, &sid);
+		if (error == -EINVAL && !strcmp(name, "fscreate")) {
+			if (!capable(CAP_MAC_ADMIN))
+				return error;
+			error = security_context_to_sid_force(value, size,
+							      &sid);
+		}
 		if (error)
 			return error;
 	}
@@ -5186,12 +5240,12 @@
 			struct task_struct *g, *t;
 			struct mm_struct *mm = p->mm;
 			read_lock(&tasklist_lock);
-			do_each_thread(g, t)
+			do_each_thread(g, t) {
 				if (t->mm == mm && t != p) {
 					read_unlock(&tasklist_lock);
 					return -EPERM;
 				}
-			while_each_thread(g, t);
+			} while_each_thread(g, t);
 			read_unlock(&tasklist_lock);
 		}
 
@@ -5343,10 +5397,10 @@
 	.sb_free_security =		selinux_sb_free_security,
 	.sb_copy_data =			selinux_sb_copy_data,
 	.sb_kern_mount =		selinux_sb_kern_mount,
+	.sb_show_options =		selinux_sb_show_options,
 	.sb_statfs =			selinux_sb_statfs,
 	.sb_mount =			selinux_mount,
 	.sb_umount =			selinux_umount,
-	.sb_get_mnt_opts =		selinux_get_mnt_opts,
 	.sb_set_mnt_opts =		selinux_set_mnt_opts,
 	.sb_clone_mnt_opts =		selinux_sb_clone_mnt_opts,
 	.sb_parse_opts_str = 		selinux_parse_opts_str,
@@ -5378,7 +5432,7 @@
 	.inode_listsecurity =		selinux_inode_listsecurity,
 	.inode_need_killpriv =		selinux_inode_need_killpriv,
 	.inode_killpriv =		selinux_inode_killpriv,
-	.inode_getsecid =               selinux_inode_getsecid,
+	.inode_getsecid =		selinux_inode_getsecid,
 
 	.file_permission =		selinux_file_permission,
 	.file_alloc_security =		selinux_file_alloc_security,
@@ -5419,7 +5473,7 @@
 	.task_to_inode =		selinux_task_to_inode,
 
 	.ipc_permission =		selinux_ipc_permission,
-	.ipc_getsecid =                 selinux_ipc_getsecid,
+	.ipc_getsecid =			selinux_ipc_getsecid,
 
 	.msg_msg_alloc_security =	selinux_msg_msg_alloc_security,
 	.msg_msg_free_security =	selinux_msg_msg_free_security,
@@ -5443,8 +5497,6 @@
 	.sem_semctl =			selinux_sem_semctl,
 	.sem_semop =			selinux_sem_semop,
 
-	.register_security =		selinux_register_security,
-
 	.d_instantiate =		selinux_d_instantiate,
 
 	.getprocattr =			selinux_getprocattr,
@@ -5538,7 +5590,7 @@
 					    0, SLAB_PANIC, NULL);
 	avc_init();
 
-	original_ops = secondary_ops = security_ops;
+	secondary_ops = security_ops;
 	if (!secondary_ops)
 		panic("SELinux: No initial security operations\n");
 	if (register_security(&selinux_ops))
diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h
index 6c8b9ef..1bdf973 100644
--- a/security/selinux/include/audit.h
+++ b/security/selinux/include/audit.h
@@ -1,7 +1,7 @@
 /*
  * SELinux support for the Audit LSM hooks
  *
- * Most of below header was moved from include/linux/selinux.h which 
+ * Most of below header was moved from include/linux/selinux.h which
  * is released under below copyrights:
  *
  * Author: James Morris <jmorris@redhat.com>
@@ -52,7 +52,7 @@
  *	-errno on failure.
  */
 int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule,
-                             struct audit_context *actx);
+			     struct audit_context *actx);
 
 /**
  *	selinux_audit_rule_known - check to see if rule contains selinux fields.
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 8e23d7a..7b9769f 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -75,13 +75,12 @@
 
 /* Initialize an AVC audit data structure. */
 #define AVC_AUDIT_DATA_INIT(_d,_t) \
-        { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
+	{ memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
 
 /*
  * AVC statistics
  */
-struct avc_cache_stats
-{
+struct avc_cache_stats {
 	unsigned int lookups;
 	unsigned int hits;
 	unsigned int misses;
@@ -97,8 +96,8 @@
 void __init avc_init(void);
 
 void avc_audit(u32 ssid, u32 tsid,
-               u16 tclass, u32 requested,
-               struct av_decision *avd, int result, struct avc_audit_data *auditdata);
+	       u16 tclass, u32 requested,
+	       struct av_decision *avd, int result, struct avc_audit_data *auditdata);
 
 #define AVC_STRICT 1 /* Ignore permissive mode. */
 int avc_has_perm_noaudit(u32 ssid, u32 tsid,
@@ -107,8 +106,8 @@
 			 struct av_decision *avd);
 
 int avc_has_perm(u32 ssid, u32 tsid,
-                 u16 tclass, u32 requested,
-                 struct avc_audit_data *auditdata);
+		 u16 tclass, u32 requested,
+		 struct avc_audit_data *auditdata);
 
 u32 avc_policy_seqno(void);
 
@@ -122,7 +121,7 @@
 #define AVC_CALLBACK_AUDITDENY_DISABLE	128
 
 int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
-                                     u16 tclass, u32 perms,
+				     u16 tclass, u32 perms,
 				     u32 *out_retained),
 		     u32 events, u32 ssid, u32 tsid,
 		     u16 tclass, u32 perms);
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 032c235..91070ab 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -44,7 +44,6 @@
 	u16 sclass;		/* security class of this object */
 	unsigned char initialized;	/* initialization flag */
 	struct mutex lock;
-	unsigned char inherit;	/* inherit SID from parent entry */
 };
 
 struct file_security_struct {
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index ad30ac4..44cba2e 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -93,12 +93,17 @@
 int security_sid_to_context(u32 sid, char **scontext,
 	u32 *scontext_len);
 
+int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);
+
 int security_context_to_sid(const char *scontext, u32 scontext_len,
 	u32 *out_sid);
 
 int security_context_to_sid_default(const char *scontext, u32 scontext_len,
 				    u32 *out_sid, u32 def_sid, gfp_t gfp_flags);
 
+int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+				  u32 *sid);
+
 int security_get_user_sids(u32 callsid, char *username,
 			   u32 **sids, u32 *nel);
 
@@ -131,7 +136,7 @@
 #define SECURITY_FS_USE_MNTPOINT	6 /* use mountpoint labeling */
 
 int security_fs_use(const char *fstype, unsigned int *behavior,
-	u32 *sid);
+	u32 *sid, bool can_xattr);
 
 int security_genfs_sid(const char *fstype, char *name, u16 sclass,
 	u32 *sid);
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index b6ccd09..7100072 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -38,7 +38,6 @@
 #include <linux/ipv6.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
-#include <asm/bug.h>
 
 #include "netnode.h"
 #include "objsec.h"
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 90b4cff..fe7fba6 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -37,7 +37,6 @@
 #include <linux/ipv6.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
-#include <asm/bug.h>
 
 #include "netport.h"
 #include "objsec.h"
@@ -272,7 +271,7 @@
 	}
 
 	ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET,
-	                       SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+			       SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
 	if (ret != 0)
 		panic("avc_add_callback() failed, error %d\n", ret);
 
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index ac1ccc1..69c9dcc 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -27,7 +27,7 @@
 #include <linux/seq_file.h>
 #include <linux/percpu.h>
 #include <linux/audit.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 
 /* selinuxfs pseudo filesystem for exporting the security policy API.
    Based on the proc code and the fs/nfsd/nfsctl.c code. */
@@ -57,14 +57,18 @@
 
 static int __init checkreqprot_setup(char *str)
 {
-	selinux_checkreqprot = simple_strtoul(str, NULL, 0) ? 1 : 0;
+	unsigned long checkreqprot;
+	if (!strict_strtoul(str, 0, &checkreqprot))
+		selinux_checkreqprot = checkreqprot ? 1 : 0;
 	return 1;
 }
 __setup("checkreqprot=", checkreqprot_setup);
 
 static int __init selinux_compat_net_setup(char *str)
 {
-	selinux_compat_net = simple_strtoul(str, NULL, 0) ? 1 : 0;
+	unsigned long compat_net;
+	if (!strict_strtoul(str, 0, &compat_net))
+		selinux_compat_net = compat_net ? 1 : 0;
 	return 1;
 }
 __setup("selinux_compat_net=", selinux_compat_net_setup);
@@ -352,11 +356,6 @@
 		length = count;
 
 out1:
-
-	printk(KERN_INFO "SELinux: policy loaded with handle_unknown=%s\n",
-	       (security_get_reject_unknown() ? "reject" :
-		(security_get_allow_unknown() ? "allow" : "deny")));
-
 	audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
 		"policy loaded auid=%u ses=%u",
 		audit_get_loginuid(current),
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 9e66263..a1be97f 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -311,7 +311,7 @@
 	}
 
 	printk(KERN_DEBUG "SELinux: %s:  %d entries and %d/%d buckets used, "
-	       "longest chain length %d sum of chain length^2 %Lu\n",
+	       "longest chain length %d sum of chain length^2 %llu\n",
 	       tag, h->nel, slots_used, h->nslot, max_chain_len,
 	       chain2_len_sum);
 }
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index b9a6f7f..658c2bd 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -28,6 +28,8 @@
 	u32 role;
 	u32 type;
 	struct mls_range range;
+	char *str;	/* string representation if context cannot be mapped. */
+	u32 len;        /* length of string in bytes */
 };
 
 static inline void mls_context_init(struct context *c)
@@ -106,20 +108,43 @@
 
 static inline int context_cpy(struct context *dst, struct context *src)
 {
+	int rc;
+
 	dst->user = src->user;
 	dst->role = src->role;
 	dst->type = src->type;
-	return mls_context_cpy(dst, src);
+	if (src->str) {
+		dst->str = kstrdup(src->str, GFP_ATOMIC);
+		if (!dst->str)
+			return -ENOMEM;
+		dst->len = src->len;
+	} else {
+		dst->str = NULL;
+		dst->len = 0;
+	}
+	rc = mls_context_cpy(dst, src);
+	if (rc) {
+		kfree(dst->str);
+		return rc;
+	}
+	return 0;
 }
 
 static inline void context_destroy(struct context *c)
 {
 	c->user = c->role = c->type = 0;
+	kfree(c->str);
+	c->str = NULL;
+	c->len = 0;
 	mls_context_destroy(c);
 }
 
 static inline int context_cmp(struct context *c1, struct context *c2)
 {
+	if (c1->len && c2->len)
+		return (c1->len == c2->len && !strcmp(c1->str, c2->str));
+	if (c1->len || c2->len)
+		return 0;
 	return ((c1->user == c2->user) &&
 		(c1->role == c2->role) &&
 		(c1->type == c2->type) &&
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 8b1706b..77d745d 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -239,7 +239,8 @@
  * Policy read-lock must be held for sidtab lookup.
  *
  */
-int mls_context_to_sid(char oldc,
+int mls_context_to_sid(struct policydb *pol,
+		       char oldc,
 		       char **scontext,
 		       struct context *context,
 		       struct sidtab *s,
@@ -286,7 +287,7 @@
 		*p++ = 0;
 
 	for (l = 0; l < 2; l++) {
-		levdatum = hashtab_search(policydb.p_levels.table, scontextp);
+		levdatum = hashtab_search(pol->p_levels.table, scontextp);
 		if (!levdatum) {
 			rc = -EINVAL;
 			goto out;
@@ -311,7 +312,7 @@
 					*rngptr++ = 0;
 				}
 
-				catdatum = hashtab_search(policydb.p_cats.table,
+				catdatum = hashtab_search(pol->p_cats.table,
 							  scontextp);
 				if (!catdatum) {
 					rc = -EINVAL;
@@ -327,7 +328,7 @@
 				if (rngptr) {
 					int i;
 
-					rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
+					rngdatum = hashtab_search(pol->p_cats.table, rngptr);
 					if (!rngdatum) {
 						rc = -EINVAL;
 						goto out;
@@ -395,7 +396,7 @@
 	if (!tmpstr) {
 		rc = -ENOMEM;
 	} else {
-		rc = mls_context_to_sid(':', &tmpstr, context,
+		rc = mls_context_to_sid(&policydb, ':', &tmpstr, context,
 					NULL, SECSID_NULL);
 		kfree(freestr);
 	}
@@ -436,13 +437,13 @@
 		struct mls_level *usercon_clr = &(usercon->range.level[1]);
 
 		/* Honor the user's default level if we can */
-		if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
+		if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
 			*usercon_sen = *user_def;
-		} else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
+		else if (mls_level_between(fromcon_sen, user_def, user_clr))
 			*usercon_sen = *fromcon_sen;
-		} else if (mls_level_between(fromcon_clr, user_low, user_def)) {
+		else if (mls_level_between(fromcon_clr, user_low, user_def))
 			*usercon_sen = *user_low;
-		} else
+		else
 			return -EINVAL;
 
 		/* Lower the clearance of available contexts
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 0fdf625..1276715 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -30,7 +30,8 @@
 int mls_range_isvalid(struct policydb *p, struct mls_range *r);
 int mls_level_isvalid(struct policydb *p, struct mls_level *l);
 
-int mls_context_to_sid(char oldc,
+int mls_context_to_sid(struct policydb *p,
+		       char oldc,
 		       char **scontext,
 		       struct context *context,
 		       struct sidtab *s,
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 84f8cc7..2391761 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1478,7 +1478,8 @@
 	struct ocontext *l, *c, *newc;
 	struct genfs *genfs_p, *genfs, *newgenfs;
 	int i, j, rc;
-	__le32 buf[8];
+	__le32 buf[4];
+	u32 nodebuf[8];
 	u32 len, len2, config, nprim, nel, nel2;
 	char *policydb_str;
 	struct policydb_compat_info *info;
@@ -1749,11 +1750,11 @@
 					goto bad;
 				break;
 			case OCON_NODE:
-				rc = next_entry(buf, fp, sizeof(u32) * 2);
+				rc = next_entry(nodebuf, fp, sizeof(u32) * 2);
 				if (rc < 0)
 					goto bad;
-				c->u.node.addr = le32_to_cpu(buf[0]);
-				c->u.node.mask = le32_to_cpu(buf[1]);
+				c->u.node.addr = nodebuf[0]; /* network order */
+				c->u.node.mask = nodebuf[1]; /* network order */
 				rc = context_read_and_validate(&c->context[0], p, fp);
 				if (rc)
 					goto bad;
@@ -1782,13 +1783,13 @@
 			case OCON_NODE6: {
 				int k;
 
-				rc = next_entry(buf, fp, sizeof(u32) * 8);
+				rc = next_entry(nodebuf, fp, sizeof(u32) * 8);
 				if (rc < 0)
 					goto bad;
 				for (k = 0; k < 4; k++)
-					c->u.node6.addr[k] = le32_to_cpu(buf[k]);
+					c->u.node6.addr[k] = nodebuf[k];
 				for (k = 0; k < 4; k++)
-					c->u.node6.mask[k] = le32_to_cpu(buf[k+4]);
+					c->u.node6.mask[k] = nodebuf[k+4];
 				if (context_read_and_validate(&c->context[0], p, fp))
 					goto bad;
 				break;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index dcc2e1c..8e42da1 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -71,14 +71,6 @@
 extern const struct selinux_class_perm selinux_class_perm;
 
 static DEFINE_RWLOCK(policy_rwlock);
-#define POLICY_RDLOCK read_lock(&policy_rwlock)
-#define POLICY_WRLOCK write_lock_irq(&policy_rwlock)
-#define POLICY_RDUNLOCK read_unlock(&policy_rwlock)
-#define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock)
-
-static DEFINE_MUTEX(load_mutex);
-#define LOAD_LOCK mutex_lock(&load_mutex)
-#define LOAD_UNLOCK mutex_unlock(&load_mutex)
 
 static struct sidtab sidtab;
 struct policydb policydb;
@@ -332,7 +324,7 @@
 		goto inval_class;
 	if (unlikely(tclass > policydb.p_classes.nprim))
 		if (tclass > kdefs->cts_len ||
-		    !kdefs->class_to_string[tclass - 1] ||
+		    !kdefs->class_to_string[tclass] ||
 		    !policydb.allow_unknown)
 			goto inval_class;
 
@@ -415,9 +407,19 @@
 	return 0;
 
 inval_class:
-	printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n", __func__,
-		tclass);
-	return -EINVAL;
+	if (!tclass || tclass > kdefs->cts_len ||
+	    !kdefs->class_to_string[tclass]) {
+		if (printk_ratelimit())
+			printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n",
+			       __func__, tclass);
+		return -EINVAL;
+	}
+
+	/*
+	 * Known to the kernel, but not to the policy.
+	 * Handle as a denial (allowed is 0).
+	 */
+	return 0;
 }
 
 /*
@@ -429,7 +431,7 @@
 	u32 type;
 	int rc;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	context = sidtab_search(&sidtab, sid);
 	BUG_ON(!context);
@@ -441,7 +443,7 @@
 	 */
 	rc = ebitmap_get_bit(&policydb.permissive_map, type);
 
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -486,7 +488,7 @@
 	if (!ss_initialized)
 		return 0;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	/*
 	 * Remap extended Netlink classes for old policy versions.
@@ -543,7 +545,7 @@
 	}
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -578,7 +580,7 @@
 		return 0;
 	}
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	scontext = sidtab_search(&sidtab, ssid);
 	if (!scontext) {
@@ -598,7 +600,7 @@
 	rc = context_struct_compute_av(scontext, tcontext, tclass,
 				       requested, avd);
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -616,6 +618,14 @@
 	*scontext = NULL;
 	*scontext_len = 0;
 
+	if (context->len) {
+		*scontext_len = context->len;
+		*scontext = kstrdup(context->str, GFP_ATOMIC);
+		if (!(*scontext))
+			return -ENOMEM;
+		return 0;
+	}
+
 	/* Compute the size of the context. */
 	*scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
 	*scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
@@ -655,17 +665,8 @@
 	return initial_sid_to_string[sid];
 }
 
-/**
- * security_sid_to_context - Obtain a context for a given SID.
- * @sid: security identifier, SID
- * @scontext: security context
- * @scontext_len: length in bytes
- *
- * Write the string representation of the context associated with @sid
- * into a dynamically allocated string of the correct size.  Set @scontext
- * to point to this string and set @scontext_len to the length of the string.
- */
-int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
+static int security_sid_to_context_core(u32 sid, char **scontext,
+					u32 *scontext_len, int force)
 {
 	struct context *context;
 	int rc = 0;
@@ -692,8 +693,11 @@
 		rc = -EINVAL;
 		goto out;
 	}
-	POLICY_RDLOCK;
-	context = sidtab_search(&sidtab, sid);
+	read_lock(&policy_rwlock);
+	if (force)
+		context = sidtab_search_force(&sidtab, sid);
+	else
+		context = sidtab_search(&sidtab, sid);
 	if (!context) {
 		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
 			__func__, sid);
@@ -702,59 +706,54 @@
 	}
 	rc = context_struct_to_string(context, scontext, scontext_len);
 out_unlock:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 out:
 	return rc;
 
 }
 
-static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
-					u32 *sid, u32 def_sid, gfp_t gfp_flags)
+/**
+ * security_sid_to_context - Obtain a context for a given SID.
+ * @sid: security identifier, SID
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ *
+ * Write the string representation of the context associated with @sid
+ * into a dynamically allocated string of the correct size.  Set @scontext
+ * to point to this string and set @scontext_len to the length of the string.
+ */
+int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
 {
-	char *scontext2;
-	struct context context;
+	return security_sid_to_context_core(sid, scontext, scontext_len, 0);
+}
+
+int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len)
+{
+	return security_sid_to_context_core(sid, scontext, scontext_len, 1);
+}
+
+/*
+ * Caveat:  Mutates scontext.
+ */
+static int string_to_context_struct(struct policydb *pol,
+				    struct sidtab *sidtabp,
+				    char *scontext,
+				    u32 scontext_len,
+				    struct context *ctx,
+				    u32 def_sid)
+{
 	struct role_datum *role;
 	struct type_datum *typdatum;
 	struct user_datum *usrdatum;
 	char *scontextp, *p, oldc;
 	int rc = 0;
 
-	if (!ss_initialized) {
-		int i;
-
-		for (i = 1; i < SECINITSID_NUM; i++) {
-			if (!strcmp(initial_sid_to_string[i], scontext)) {
-				*sid = i;
-				goto out;
-			}
-		}
-		*sid = SECINITSID_KERNEL;
-		goto out;
-	}
-	*sid = SECSID_NULL;
-
-	/* Copy the string so that we can modify the copy as we parse it.
-	   The string should already by null terminated, but we append a
-	   null suffix to the copy to avoid problems with the existing
-	   attr package, which doesn't view the null terminator as part
-	   of the attribute value. */
-	scontext2 = kmalloc(scontext_len+1, gfp_flags);
-	if (!scontext2) {
-		rc = -ENOMEM;
-		goto out;
-	}
-	memcpy(scontext2, scontext, scontext_len);
-	scontext2[scontext_len] = 0;
-
-	context_init(&context);
-	*sid = SECSID_NULL;
-
-	POLICY_RDLOCK;
+	context_init(ctx);
 
 	/* Parse the security context. */
 
 	rc = -EINVAL;
-	scontextp = (char *) scontext2;
+	scontextp = (char *) scontext;
 
 	/* Extract the user. */
 	p = scontextp;
@@ -762,15 +761,15 @@
 		p++;
 
 	if (*p == 0)
-		goto out_unlock;
+		goto out;
 
 	*p++ = 0;
 
-	usrdatum = hashtab_search(policydb.p_users.table, scontextp);
+	usrdatum = hashtab_search(pol->p_users.table, scontextp);
 	if (!usrdatum)
-		goto out_unlock;
+		goto out;
 
-	context.user = usrdatum->value;
+	ctx->user = usrdatum->value;
 
 	/* Extract role. */
 	scontextp = p;
@@ -778,14 +777,14 @@
 		p++;
 
 	if (*p == 0)
-		goto out_unlock;
+		goto out;
 
 	*p++ = 0;
 
-	role = hashtab_search(policydb.p_roles.table, scontextp);
+	role = hashtab_search(pol->p_roles.table, scontextp);
 	if (!role)
-		goto out_unlock;
-	context.role = role->value;
+		goto out;
+	ctx->role = role->value;
 
 	/* Extract type. */
 	scontextp = p;
@@ -794,36 +793,90 @@
 	oldc = *p;
 	*p++ = 0;
 
-	typdatum = hashtab_search(policydb.p_types.table, scontextp);
+	typdatum = hashtab_search(pol->p_types.table, scontextp);
 	if (!typdatum)
-		goto out_unlock;
+		goto out;
 
-	context.type = typdatum->value;
+	ctx->type = typdatum->value;
 
-	rc = mls_context_to_sid(oldc, &p, &context, &sidtab, def_sid);
+	rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid);
 	if (rc)
-		goto out_unlock;
+		goto out;
 
-	if ((p - scontext2) < scontext_len) {
+	if ((p - scontext) < scontext_len) {
 		rc = -EINVAL;
-		goto out_unlock;
+		goto out;
 	}
 
 	/* Check the validity of the new context. */
-	if (!policydb_context_isvalid(&policydb, &context)) {
+	if (!policydb_context_isvalid(pol, ctx)) {
 		rc = -EINVAL;
-		goto out_unlock;
+		context_destroy(ctx);
+		goto out;
 	}
-	/* Obtain the new sid. */
-	rc = sidtab_context_to_sid(&sidtab, &context, sid);
-out_unlock:
-	POLICY_RDUNLOCK;
-	context_destroy(&context);
-	kfree(scontext2);
+	rc = 0;
 out:
 	return rc;
 }
 
+static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
+					u32 *sid, u32 def_sid, gfp_t gfp_flags,
+					int force)
+{
+	char *scontext2, *str = NULL;
+	struct context context;
+	int rc = 0;
+
+	if (!ss_initialized) {
+		int i;
+
+		for (i = 1; i < SECINITSID_NUM; i++) {
+			if (!strcmp(initial_sid_to_string[i], scontext)) {
+				*sid = i;
+				return 0;
+			}
+		}
+		*sid = SECINITSID_KERNEL;
+		return 0;
+	}
+	*sid = SECSID_NULL;
+
+	/* Copy the string so that we can modify the copy as we parse it. */
+	scontext2 = kmalloc(scontext_len+1, gfp_flags);
+	if (!scontext2)
+		return -ENOMEM;
+	memcpy(scontext2, scontext, scontext_len);
+	scontext2[scontext_len] = 0;
+
+	if (force) {
+		/* Save another copy for storing in uninterpreted form */
+		str = kstrdup(scontext2, gfp_flags);
+		if (!str) {
+			kfree(scontext2);
+			return -ENOMEM;
+		}
+	}
+
+	read_lock(&policy_rwlock);
+	rc = string_to_context_struct(&policydb, &sidtab,
+				      scontext2, scontext_len,
+				      &context, def_sid);
+	if (rc == -EINVAL && force) {
+		context.str = str;
+		context.len = scontext_len;
+		str = NULL;
+	} else if (rc)
+		goto out;
+	rc = sidtab_context_to_sid(&sidtab, &context, sid);
+	if (rc)
+		context_destroy(&context);
+out:
+	read_unlock(&policy_rwlock);
+	kfree(scontext2);
+	kfree(str);
+	return rc;
+}
+
 /**
  * security_context_to_sid - Obtain a SID for a given security context.
  * @scontext: security context
@@ -838,7 +891,7 @@
 int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
 {
 	return security_context_to_sid_core(scontext, scontext_len,
-					    sid, SECSID_NULL, GFP_KERNEL);
+					    sid, SECSID_NULL, GFP_KERNEL, 0);
 }
 
 /**
@@ -855,6 +908,7 @@
  * The default SID is passed to the MLS layer to be used to allow
  * kernel labeling of the MLS field if the MLS field is not present
  * (for upgrading to MLS without full relabel).
+ * Implicitly forces adding of the context even if it cannot be mapped yet.
  * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
  * memory is available, or 0 on success.
  */
@@ -862,7 +916,14 @@
 				    u32 *sid, u32 def_sid, gfp_t gfp_flags)
 {
 	return security_context_to_sid_core(scontext, scontext_len,
-					    sid, def_sid, gfp_flags);
+					    sid, def_sid, gfp_flags, 1);
+}
+
+int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+				  u32 *sid)
+{
+	return security_context_to_sid_core(scontext, scontext_len,
+					    sid, SECSID_NULL, GFP_KERNEL, 1);
 }
 
 static int compute_sid_handle_invalid_context(
@@ -922,7 +983,7 @@
 
 	context_init(&newcontext);
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	scontext = sidtab_search(&sidtab, ssid);
 	if (!scontext) {
@@ -1027,7 +1088,7 @@
 	/* Obtain the sid for the context. */
 	rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
 out_unlock:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	context_destroy(&newcontext);
 out:
 	return rc;
@@ -1110,6 +1171,7 @@
 	const struct selinux_class_perm *kdefs = &selinux_class_perm;
 	const char *def_class, *def_perm, *pol_class;
 	struct symtab *perms;
+	bool print_unknown_handle = 0;
 
 	if (p->allow_unknown) {
 		u32 num_classes = kdefs->cts_len;
@@ -1130,6 +1192,7 @@
 				return -EINVAL;
 			if (p->allow_unknown)
 				p->undefined_perms[i-1] = ~0U;
+			print_unknown_handle = 1;
 			continue;
 		}
 		pol_class = p->p_class_val_to_name[i-1];
@@ -1159,6 +1222,7 @@
 				return -EINVAL;
 			if (p->allow_unknown)
 				p->undefined_perms[class_val-1] |= perm_val;
+			print_unknown_handle = 1;
 			continue;
 		}
 		perdatum = hashtab_search(perms->table, def_perm);
@@ -1206,6 +1270,7 @@
 					return -EINVAL;
 				if (p->allow_unknown)
 					p->undefined_perms[class_val-1] |= (1 << j);
+				print_unknown_handle = 1;
 				continue;
 			}
 			perdatum = hashtab_search(perms->table, def_perm);
@@ -1223,6 +1288,9 @@
 			}
 		}
 	}
+	if (print_unknown_handle)
+		printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n",
+			(security_get_allow_unknown() ? "allowed" : "denied"));
 	return 0;
 }
 
@@ -1246,9 +1314,12 @@
 		char *s;
 		u32 len;
 
-		context_struct_to_string(context, &s, &len);
-		printk(KERN_ERR "SELinux:  context %s is invalid\n", s);
-		kfree(s);
+		if (!context_struct_to_string(context, &s, &len)) {
+			printk(KERN_WARNING
+		       "SELinux:  Context %s would be invalid if enforcing\n",
+			       s);
+			kfree(s);
+		}
 	}
 	return rc;
 }
@@ -1280,6 +1351,37 @@
 
 	args = p;
 
+	if (c->str) {
+		struct context ctx;
+		s = kstrdup(c->str, GFP_KERNEL);
+		if (!s) {
+			rc = -ENOMEM;
+			goto out;
+		}
+		rc = string_to_context_struct(args->newp, NULL, s,
+					      c->len, &ctx, SECSID_NULL);
+		kfree(s);
+		if (!rc) {
+			printk(KERN_INFO
+		       "SELinux:  Context %s became valid (mapped).\n",
+			       c->str);
+			/* Replace string with mapped representation. */
+			kfree(c->str);
+			memcpy(c, &ctx, sizeof(*c));
+			goto out;
+		} else if (rc == -EINVAL) {
+			/* Retain string representation for later mapping. */
+			rc = 0;
+			goto out;
+		} else {
+			/* Other error condition, e.g. ENOMEM. */
+			printk(KERN_ERR
+		       "SELinux:   Unable to map context %s, rc = %d.\n",
+			       c->str, -rc);
+			goto out;
+		}
+	}
+
 	rc = context_cpy(&oldc, c);
 	if (rc)
 		goto out;
@@ -1319,13 +1421,21 @@
 	}
 
 	context_destroy(&oldc);
+	rc = 0;
 out:
 	return rc;
 bad:
-	context_struct_to_string(&oldc, &s, &len);
+	/* Map old representation to string and save it. */
+	if (context_struct_to_string(&oldc, &s, &len))
+		return -ENOMEM;
 	context_destroy(&oldc);
-	printk(KERN_ERR "SELinux:  invalidating context %s\n", s);
-	kfree(s);
+	context_destroy(c);
+	c->str = s;
+	c->len = len;
+	printk(KERN_INFO
+	       "SELinux:  Context %s became invalid (unmapped).\n",
+	       c->str);
+	rc = 0;
 	goto out;
 }
 
@@ -1359,17 +1469,13 @@
 	int rc = 0;
 	struct policy_file file = { data, len }, *fp = &file;
 
-	LOAD_LOCK;
-
 	if (!ss_initialized) {
 		avtab_cache_init();
 		if (policydb_read(&policydb, fp)) {
-			LOAD_UNLOCK;
 			avtab_cache_destroy();
 			return -EINVAL;
 		}
 		if (policydb_load_isids(&policydb, &sidtab)) {
-			LOAD_UNLOCK;
 			policydb_destroy(&policydb);
 			avtab_cache_destroy();
 			return -EINVAL;
@@ -1378,7 +1484,6 @@
 		if (validate_classes(&policydb)) {
 			printk(KERN_ERR
 			       "SELinux:  the definition of a class is incorrect\n");
-			LOAD_UNLOCK;
 			sidtab_destroy(&sidtab);
 			policydb_destroy(&policydb);
 			avtab_cache_destroy();
@@ -1388,7 +1493,6 @@
 		policydb_loaded_version = policydb.policyvers;
 		ss_initialized = 1;
 		seqno = ++latest_granting;
-		LOAD_UNLOCK;
 		selinux_complete_init();
 		avc_ss_reset(seqno);
 		selnl_notify_policyload(seqno);
@@ -1401,12 +1505,13 @@
 	sidtab_hash_eval(&sidtab, "sids");
 #endif
 
-	if (policydb_read(&newpolicydb, fp)) {
-		LOAD_UNLOCK;
+	if (policydb_read(&newpolicydb, fp))
 		return -EINVAL;
-	}
 
-	sidtab_init(&newsidtab);
+	if (sidtab_init(&newsidtab)) {
+		policydb_destroy(&newpolicydb);
+		return -ENOMEM;
+	}
 
 	/* Verify that the kernel defined classes are correct. */
 	if (validate_classes(&newpolicydb)) {
@@ -1429,25 +1534,28 @@
 		goto err;
 	}
 
-	/* Convert the internal representations of contexts
-	   in the new SID table and remove invalid SIDs. */
+	/*
+	 * Convert the internal representations of contexts
+	 * in the new SID table.
+	 */
 	args.oldp = &policydb;
 	args.newp = &newpolicydb;
-	sidtab_map_remove_on_error(&newsidtab, convert_context, &args);
+	rc = sidtab_map(&newsidtab, convert_context, &args);
+	if (rc)
+		goto err;
 
 	/* Save the old policydb and SID table to free later. */
 	memcpy(&oldpolicydb, &policydb, sizeof policydb);
 	sidtab_set(&oldsidtab, &sidtab);
 
 	/* Install the new policydb and SID table. */
-	POLICY_WRLOCK;
+	write_lock_irq(&policy_rwlock);
 	memcpy(&policydb, &newpolicydb, sizeof policydb);
 	sidtab_set(&sidtab, &newsidtab);
 	security_load_policycaps();
 	seqno = ++latest_granting;
 	policydb_loaded_version = policydb.policyvers;
-	POLICY_WRUNLOCK;
-	LOAD_UNLOCK;
+	write_unlock_irq(&policy_rwlock);
 
 	/* Free the old policydb and SID table. */
 	policydb_destroy(&oldpolicydb);
@@ -1461,7 +1569,6 @@
 	return 0;
 
 err:
-	LOAD_UNLOCK;
 	sidtab_destroy(&newsidtab);
 	policydb_destroy(&newpolicydb);
 	return rc;
@@ -1479,7 +1586,7 @@
 	struct ocontext *c;
 	int rc = 0;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	c = policydb.ocontexts[OCON_PORT];
 	while (c) {
@@ -1504,7 +1611,7 @@
 	}
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -1518,7 +1625,7 @@
 	int rc = 0;
 	struct ocontext *c;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	c = policydb.ocontexts[OCON_NETIF];
 	while (c) {
@@ -1545,7 +1652,7 @@
 		*if_sid = SECINITSID_NETIF;
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -1577,7 +1684,7 @@
 	int rc = 0;
 	struct ocontext *c;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	switch (domain) {
 	case AF_INET: {
@@ -1632,7 +1739,7 @@
 	}
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -1671,7 +1778,9 @@
 	if (!ss_initialized)
 		goto out;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
+
+	context_init(&usercon);
 
 	fromcon = sidtab_search(&sidtab, fromsid);
 	if (!fromcon) {
@@ -1722,7 +1831,7 @@
 	}
 
 out_unlock:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	if (rc || !mynel) {
 		kfree(mysids);
 		goto out;
@@ -1775,7 +1884,7 @@
 	while (path[0] == '/' && path[1] == '/')
 		path++;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
 		cmp = strcmp(fstype, genfs->fstype);
@@ -1812,7 +1921,7 @@
 
 	*sid = c->sid[0];
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -1825,12 +1934,13 @@
 int security_fs_use(
 	const char *fstype,
 	unsigned int *behavior,
-	u32 *sid)
+	u32 *sid,
+	bool can_xattr)
 {
 	int rc = 0;
 	struct ocontext *c;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	c = policydb.ocontexts[OCON_FSUSE];
 	while (c) {
@@ -1839,6 +1949,7 @@
 		c = c->next;
 	}
 
+	/* look for labeling behavior defined in policy */
 	if (c) {
 		*behavior = c->v.behavior;
 		if (!c->sid[0]) {
@@ -1849,18 +1960,27 @@
 				goto out;
 		}
 		*sid = c->sid[0];
+		goto out;
+	}
+
+	/* labeling behavior not in policy, use xattrs if possible */
+	if (can_xattr) {
+		*behavior = SECURITY_FS_USE_XATTR;
+		*sid = SECINITSID_FS;
+		goto out;
+	}
+
+	/* no behavior in policy and can't use xattrs, try GENFS */
+	rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
+	if (rc) {
+		*behavior = SECURITY_FS_USE_NONE;
+		rc = 0;
 	} else {
-		rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
-		if (rc) {
-			*behavior = SECURITY_FS_USE_NONE;
-			rc = 0;
-		} else {
-			*behavior = SECURITY_FS_USE_GENFS;
-		}
+		*behavior = SECURITY_FS_USE_GENFS;
 	}
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -1868,7 +1988,7 @@
 {
 	int i, rc = -ENOMEM;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 	*names = NULL;
 	*values = NULL;
 
@@ -1898,7 +2018,7 @@
 	}
 	rc = 0;
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 err:
 	if (*names) {
@@ -1916,7 +2036,7 @@
 	int lenp, seqno = 0;
 	struct cond_node *cur;
 
-	POLICY_WRLOCK;
+	write_lock_irq(&policy_rwlock);
 
 	lenp = policydb.p_bools.nprim;
 	if (len != lenp) {
@@ -1950,7 +2070,7 @@
 	seqno = ++latest_granting;
 
 out:
-	POLICY_WRUNLOCK;
+	write_unlock_irq(&policy_rwlock);
 	if (!rc) {
 		avc_ss_reset(seqno);
 		selnl_notify_policyload(seqno);
@@ -1964,7 +2084,7 @@
 	int rc = 0;
 	int len;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	len = policydb.p_bools.nprim;
 	if (bool >= len) {
@@ -1974,7 +2094,7 @@
 
 	rc = policydb.bool_val_to_struct[bool]->state;
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -2029,7 +2149,7 @@
 
 	context_init(&newcon);
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 	context1 = sidtab_search(&sidtab, sid);
 	if (!context1) {
 		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
@@ -2071,7 +2191,7 @@
 	}
 
 out_unlock:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	context_destroy(&newcon);
 out:
 	return rc;
@@ -2128,7 +2248,7 @@
 		return 0;
 	}
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
 	if (!nlbl_ctx) {
@@ -2147,7 +2267,7 @@
 	rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
 
 out_slowpath:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	if (rc == 0)
 		/* at present NetLabel SIDs/labels really only carry MLS
 		 * information so if the MLS portion of the NetLabel SID
@@ -2177,7 +2297,7 @@
 {
 	int rc = -ENOMEM;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	*nclasses = policydb.p_classes.nprim;
 	*classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
@@ -2194,7 +2314,7 @@
 	}
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 
@@ -2216,7 +2336,7 @@
 	int rc = -ENOMEM, i;
 	struct class_datum *match;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	match = hashtab_search(policydb.p_classes.table, class);
 	if (!match) {
@@ -2244,11 +2364,11 @@
 		goto err;
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 
 err:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	for (i = 0; i < *nperms; i++)
 		kfree((*perms)[i]);
 	kfree(*perms);
@@ -2279,9 +2399,9 @@
 {
 	int rc;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 	rc = ebitmap_get_bit(&policydb.policycaps, req_cap);
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 
 	return rc;
 }
@@ -2345,7 +2465,7 @@
 
 	context_init(&tmprule->au_ctxt);
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	tmprule->au_seqno = latest_granting;
 
@@ -2382,7 +2502,7 @@
 		break;
 	}
 
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 
 	if (rc) {
 		selinux_audit_rule_free(tmprule);
@@ -2420,7 +2540,7 @@
 }
 
 int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
-                             struct audit_context *actx)
+			     struct audit_context *actx)
 {
 	struct context *ctxt;
 	struct mls_level *level;
@@ -2433,7 +2553,7 @@
 		return -ENOENT;
 	}
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	if (rule->au_seqno < latest_granting) {
 		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
@@ -2527,14 +2647,14 @@
 	}
 
 out:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return match;
 }
 
 static int (*aurule_callback)(void) = audit_update_lsm_rules;
 
 static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
-                               u16 class, u32 perms, u32 *retained)
+			       u16 class, u32 perms, u32 *retained)
 {
 	int err = 0;
 
@@ -2615,7 +2735,7 @@
 		return 0;
 	}
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 
 	if (secattr->flags & NETLBL_SECATTR_CACHE) {
 		*sid = *(u32 *)secattr->cache->data;
@@ -2660,7 +2780,7 @@
 	}
 
 netlbl_secattr_to_sid_return:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 netlbl_secattr_to_sid_return_cleanup:
 	ebitmap_destroy(&ctx_new.range.level[0].cat);
@@ -2685,7 +2805,7 @@
 	if (!ss_initialized)
 		return 0;
 
-	POLICY_RDLOCK;
+	read_lock(&policy_rwlock);
 	ctx = sidtab_search(&sidtab, sid);
 	if (ctx == NULL)
 		goto netlbl_sid_to_secattr_failure;
@@ -2696,12 +2816,12 @@
 	rc = mls_export_netlbl_cat(ctx, secattr);
 	if (rc != 0)
 		goto netlbl_sid_to_secattr_failure;
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 
 	return 0;
 
 netlbl_sid_to_secattr_failure:
-	POLICY_RDUNLOCK;
+	read_unlock(&policy_rwlock);
 	return rc;
 }
 #endif /* CONFIG_NETLABEL */
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index 4a516ff..a81ded1 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -14,10 +14,6 @@
 #define SIDTAB_HASH(sid) \
 (sid & SIDTAB_HASH_MASK)
 
-#define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock)
-#define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x)
-#define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x)
-
 int sidtab_init(struct sidtab *s)
 {
 	int i;
@@ -30,7 +26,7 @@
 	s->nel = 0;
 	s->next_sid = 1;
 	s->shutdown = 0;
-	INIT_SIDTAB_LOCK(s);
+	spin_lock_init(&s->lock);
 	return 0;
 }
 
@@ -86,7 +82,7 @@
 	return rc;
 }
 
-struct context *sidtab_search(struct sidtab *s, u32 sid)
+static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
 {
 	int hvalue;
 	struct sidtab_node *cur;
@@ -99,7 +95,10 @@
 	while (cur != NULL && sid > cur->sid)
 		cur = cur->next;
 
-	if (cur == NULL || sid != cur->sid) {
+	if (force && cur && sid == cur->sid && cur->context.len)
+		return &cur->context;
+
+	if (cur == NULL || sid != cur->sid || cur->context.len) {
 		/* Remap invalid SIDs to the unlabeled SID. */
 		sid = SECINITSID_UNLABELED;
 		hvalue = SIDTAB_HASH(sid);
@@ -113,6 +112,16 @@
 	return &cur->context;
 }
 
+struct context *sidtab_search(struct sidtab *s, u32 sid)
+{
+	return sidtab_search_core(s, sid, 0);
+}
+
+struct context *sidtab_search_force(struct sidtab *s, u32 sid)
+{
+	return sidtab_search_core(s, sid, 1);
+}
+
 int sidtab_map(struct sidtab *s,
 	       int (*apply) (u32 sid,
 			     struct context *context,
@@ -138,43 +147,6 @@
 	return rc;
 }
 
-void sidtab_map_remove_on_error(struct sidtab *s,
-				int (*apply) (u32 sid,
-					      struct context *context,
-					      void *args),
-				void *args)
-{
-	int i, ret;
-	struct sidtab_node *last, *cur, *temp;
-
-	if (!s)
-		return;
-
-	for (i = 0; i < SIDTAB_SIZE; i++) {
-		last = NULL;
-		cur = s->htable[i];
-		while (cur != NULL) {
-			ret = apply(cur->sid, &cur->context, args);
-			if (ret) {
-				if (last)
-					last->next = cur->next;
-				else
-					s->htable[i] = cur->next;
-				temp = cur;
-				cur = cur->next;
-				context_destroy(&temp->context);
-				kfree(temp);
-				s->nel--;
-			} else {
-				last = cur;
-				cur = cur->next;
-			}
-		}
-	}
-
-	return;
-}
-
 static inline u32 sidtab_search_context(struct sidtab *s,
 						  struct context *context)
 {
@@ -204,7 +176,7 @@
 
 	sid = sidtab_search_context(s, context);
 	if (!sid) {
-		SIDTAB_LOCK(s, flags);
+		spin_lock_irqsave(&s->lock, flags);
 		/* Rescan now that we hold the lock. */
 		sid = sidtab_search_context(s, context);
 		if (sid)
@@ -215,11 +187,15 @@
 			goto unlock_out;
 		}
 		sid = s->next_sid++;
+		if (context->len)
+			printk(KERN_INFO
+		       "SELinux:  Context %s is not valid (left unmapped).\n",
+			       context->str);
 		ret = sidtab_insert(s, sid, context);
 		if (ret)
 			s->next_sid--;
 unlock_out:
-		SIDTAB_UNLOCK(s, flags);
+		spin_unlock_irqrestore(&s->lock, flags);
 	}
 
 	if (ret)
@@ -284,19 +260,19 @@
 {
 	unsigned long flags;
 
-	SIDTAB_LOCK(src, flags);
+	spin_lock_irqsave(&src->lock, flags);
 	dst->htable = src->htable;
 	dst->nel = src->nel;
 	dst->next_sid = src->next_sid;
 	dst->shutdown = 0;
-	SIDTAB_UNLOCK(src, flags);
+	spin_unlock_irqrestore(&src->lock, flags);
 }
 
 void sidtab_shutdown(struct sidtab *s)
 {
 	unsigned long flags;
 
-	SIDTAB_LOCK(s, flags);
+	spin_lock_irqsave(&s->lock, flags);
 	s->shutdown = 1;
-	SIDTAB_UNLOCK(s, flags);
+	spin_unlock_irqrestore(&s->lock, flags);
 }
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index 2fe9dfa..64ea5b1 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -32,6 +32,7 @@
 int sidtab_init(struct sidtab *s);
 int sidtab_insert(struct sidtab *s, u32 sid, struct context *context);
 struct context *sidtab_search(struct sidtab *s, u32 sid);
+struct context *sidtab_search_force(struct sidtab *s, u32 sid);
 
 int sidtab_map(struct sidtab *s,
 	       int (*apply) (u32 sid,
@@ -39,12 +40,6 @@
 			     void *args),
 	       void *args);
 
-void sidtab_map_remove_on_error(struct sidtab *s,
-				int (*apply) (u32 sid,
-					      struct context *context,
-					      void *args),
-				void *args);
-
 int sidtab_context_to_sid(struct sidtab *s,
 			  struct context *context,
 			  u32 *sid);
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 4a09293..ee5a51c 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -95,11 +95,12 @@
  *
  * Do the capability checks, and require read and write.
  */
-static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp)
+static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp,
+			unsigned int mode)
 {
 	int rc;
 
-	rc = cap_ptrace(ptp, ctp);
+	rc = cap_ptrace(ptp, ctp, mode);
 	if (rc != 0)
 		return rc;
 
@@ -1821,27 +1822,6 @@
 	*secid = smack_to_secid(smack);
 }
 
-/* module stacking operations */
-
-/**
- * smack_register_security - stack capability module
- * @name: module name
- * @ops: module operations - ignored
- *
- * Allow the capability module to register.
- */
-static int smack_register_security(const char *name,
-				   struct security_operations *ops)
-{
-	if (strcmp(name, "capability") != 0)
-		return -EINVAL;
-
-	printk(KERN_INFO "%s:  Registering secondary module %s\n",
-	       __func__, name);
-
-	return 0;
-}
-
 /**
  * smack_d_instantiate - Make sure the blob is correct on an inode
  * @opt_dentry: unused
@@ -2672,8 +2652,6 @@
 	.netlink_send =			cap_netlink_send,
 	.netlink_recv = 		cap_netlink_recv,
 
-	.register_security = 		smack_register_security,
-
 	.d_instantiate = 		smack_d_instantiate,
 
 	.getprocattr = 			smack_getprocattr,
diff --git a/sound/Kconfig b/sound/Kconfig
index 4247406..a37bee0 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -1,11 +1,9 @@
 # sound/Config.in
 #
 
-menu "Sound"
-	depends on HAS_IOMEM
-
-config SOUND
+menuconfig SOUND
 	tristate "Sound card support"
+	depends on HAS_IOMEM
 	help
 	  If you have a sound card in your computer, i.e. if it can say more
 	  than an occasional beep, say Y.  Be sure to have all the information
@@ -28,22 +26,22 @@
 	  and read <file:Documentation/sound/oss/README.modules>; the module
 	  will be called soundcore.
 
+if SOUND
+
 source "sound/oss/dmasound/Kconfig"
 
 if !M68K
 
-menu "Advanced Linux Sound Architecture"
-	depends on SOUND!=n
-
-config SND
+menuconfig SND
 	tristate "Advanced Linux Sound Architecture"
-	depends on SOUND
 	help
 	  Say 'Y' or 'M' to enable ALSA (Advanced Linux Sound Architecture),
 	  the new base sound system.
 
 	  For more information, see <http://www.alsa-project.org/>
 
+if SND
+
 source "sound/core/Kconfig"
 
 source "sound/drivers/Kconfig"
@@ -58,9 +56,7 @@
 
 source "sound/arm/Kconfig"
 
-if SPI
 source "sound/spi/Kconfig"
-endif
 
 source "sound/mips/Kconfig"
 
@@ -80,22 +76,20 @@
 
 source "sound/soc/Kconfig"
 
-endmenu
+endif # SND
 
-menu "Open Sound System"
-	depends on SOUND!=n
-
-config SOUND_PRIME
+menuconfig SOUND_PRIME
 	tristate "Open Sound System (DEPRECATED)"
-	depends on SOUND
 	help
 	  Say 'Y' or 'M' to enable Open Sound System drivers.
 
+if SOUND_PRIME
+
 source "sound/oss/Kconfig"
 
-endmenu
+endif # SOUND_PRIME
 
-endif
+endif # !M68K
 
 config AC97_BUS
 	tristate
@@ -105,4 +99,4 @@
 	  sound although they're sharing the AC97 bus. Concerned drivers
 	  should "select" this.
 
-endmenu
+endif # SOUND
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index 5d5813c..c081e18 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -1,18 +1,17 @@
-menu "Apple Onboard Audio driver"
-	depends on SND!=n && PPC_PMAC
-
-config SND_AOA
+menuconfig SND_AOA
 	tristate "Apple Onboard Audio driver"
-	depends on SND
+	depends on PPC_PMAC
 	select SND_PCM
 	---help---
 	This option enables the new driver for the various
 	Apple Onboard Audio components.
 
+if SND_AOA
+
 source "sound/aoa/fabrics/Kconfig"
 
 source "sound/aoa/codecs/Kconfig"
 
 source "sound/aoa/soundbus/Kconfig"
 
-endmenu
+endif	# SND_AOA
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig
index d5fbd60..808eb11 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -1,6 +1,5 @@
 config SND_AOA_ONYX
 	tristate "support Onyx chip"
-	depends on SND_AOA
 	select I2C
 	select I2C_POWERMAC
 	---help---
@@ -10,7 +9,6 @@
 
 #config SND_AOA_TOPAZ
 #	tristate "support Topaz chips"
-#	depends on SND_AOA
 #	---help---
 #	This option enables support for the Topaz (CS84xx)
 #	codec chips found in the latest Apple machines,
@@ -19,7 +17,6 @@
 
 config SND_AOA_TAS
 	tristate "support TAS chips"
-	depends on SND_AOA
 	select I2C
 	select I2C_POWERMAC
 	---help---
@@ -29,7 +26,6 @@
 
 config SND_AOA_TOONIE
 	tristate "support Toonie chip"
-	depends on SND_AOA
 	---help---
 	This option enables support for the toonie codec
 	found in the Mac Mini. If you have a Mac Mini and
diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig
index 50d7021..3ca475a 100644
--- a/sound/aoa/fabrics/Kconfig
+++ b/sound/aoa/fabrics/Kconfig
@@ -1,6 +1,5 @@
 config SND_AOA_FABRIC_LAYOUT
 	tristate "layout-id fabric"
-	depends on SND_AOA
 	select SND_AOA_SOUNDBUS
 	select SND_AOA_SOUNDBUS_I2S
 	---help---
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index 7368b7d..839d113 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -1,6 +1,5 @@
 config SND_AOA_SOUNDBUS
 	tristate "Apple Soundbus support"
-	depends on SOUND
 	select SND_PCM
 	---help---
 	This option enables the generic driver for the soundbus
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 2e4a5e0..351e19e 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -1,11 +1,19 @@
 # ALSA ARM drivers
 
-menu "ALSA ARM devices"
-	depends on SND!=n && ARM
+menuconfig SND_ARM
+	bool "ARM sound devices"
+	depends on ARM
+	default y
+	help
+	  Support for sound devices specific to ARM architectures.
+	  Drivers that are implemented on ASoC can be found in
+	  "ALSA for SoC audio support" section.
+
+if SND_ARM
 
 config SND_SA11XX_UDA1341
 	tristate "SA11xx UDA1341TS driver (iPaq H3600)"
-	depends on ARCH_SA1100 && SND && L3
+	depends on ARCH_SA1100 && L3
 	select SND_PCM
 	help
 	  Say Y here if you have a Compaq iPaq H3x00 handheld computer
@@ -16,7 +24,7 @@
 
 config SND_ARMAACI
 	tristate "ARM PrimeCell PL041 AC Link support"
-	depends on SND && ARM_AMBA
+	depends on ARM_AMBA
 	select SND_PCM
 	select SND_AC97_CODEC
 
@@ -26,11 +34,12 @@
 
 config SND_PXA2XX_AC97
 	tristate "AC97 driver for the Intel PXA2xx chip"
-	depends on ARCH_PXA && SND
+	depends on ARCH_PXA
 	select SND_PXA2XX_PCM
 	select SND_AC97_CODEC
 	help
 	  Say Y or M if you want to support any AC97 codec attached to
 	  the PXA2xx AC97 interface.
 
-endmenu
+endif	# SND_ARM
+
diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
index 0eff33c..faeddf3 100644
--- a/sound/arm/sa11xx-uda1341.c
+++ b/sound/arm/sa11xx-uda1341.c
@@ -21,8 +21,6 @@
  *                              merged HAL layer (patches from Brian)
  */
 
-/* $Id: sa11xx-uda1341.c,v 1.27 2005/12/07 09:13:42 cladisch Exp $ */
-
 /***************************************************************************************************
 *
 * To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index a8d71c6..335d45e 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -1,24 +1,19 @@
 # ALSA soundcard-configuration
 config SND_TIMER
 	tristate
-	depends on SND
 
 config SND_PCM
 	tristate
 	select SND_TIMER
-	depends on SND
 
 config SND_HWDEP
 	tristate
-	depends on SND
 
 config SND_RAWMIDI
 	tristate
-	depends on SND
 
 config SND_SEQUENCER
 	tristate "Sequencer support"
-	depends on SND
 	select SND_TIMER
 	help
 	  Say Y or M to enable MIDI sequencer and router support.  This
@@ -44,11 +39,9 @@
 
 config SND_OSSEMUL
 	bool
-	depends on SND
 
 config SND_MIXER_OSS
 	tristate "OSS Mixer API"
-	depends on SND
 	select SND_OSSEMUL
 	help
 	  To enable OSS mixer API emulation (/dev/mixer*), say Y here
@@ -61,7 +54,6 @@
 
 config SND_PCM_OSS
 	tristate "OSS PCM (digital audio) API"
-	depends on SND
 	select SND_OSSEMUL
 	select SND_PCM
 	help
@@ -84,7 +76,7 @@
 
 config SND_SEQUENCER_OSS
 	bool "OSS Sequencer API"
-	depends on SND && SND_SEQUENCER
+	depends on SND_SEQUENCER
 	select SND_OSSEMUL
 	help
 	  Say Y here to enable OSS sequencer emulation (both
@@ -98,7 +90,7 @@
 
 config SND_RTCTIMER
 	tristate "RTC Timer support"
-	depends on SND && RTC
+	depends on RTC
 	select SND_TIMER
 	help
 	  Say Y here to enable RTC timer support for ALSA.  ALSA uses
@@ -123,7 +115,6 @@
 
 config SND_DYNAMIC_MINORS
 	bool "Dynamic device file minor numbers"
-	depends on SND
 	help
 	  If you say Y here, the minor numbers of ALSA device files in
 	  /dev/snd/ are allocated dynamically.  This allows you to have
@@ -134,7 +125,6 @@
 
 config SND_SUPPORT_OLD_API
 	bool "Support old ALSA API"
-	depends on SND
 	default y
 	help
 	  Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
@@ -142,7 +132,7 @@
 
 config SND_VERBOSE_PROCFS
 	bool "Verbose procfs contents"
-	depends on SND && PROC_FS
+	depends on PROC_FS
 	default y
 	help
 	  Say Y here to include code for verbose procfs contents (provides
@@ -151,7 +141,6 @@
 
 config SND_VERBOSE_PRINTK
 	bool "Verbose printk"
-	depends on SND
 	help
 	  Say Y here to enable verbose log messages.  These messages
 	  will help to identify source file and position containing
@@ -161,16 +150,17 @@
 
 config SND_DEBUG
 	bool "Debug"
-	depends on SND
 	help
 	  Say Y here to enable ALSA debug code.
 
-config SND_DEBUG_DETECT
-	bool "Debug detection"
+config SND_DEBUG_VERBOSE
+	bool "More verbose debug"
 	depends on SND_DEBUG
 	help
-	  Say Y here to enable extra-verbose log messages printed when
-	  detecting devices.
+	  Say Y here to enable extra-verbose debugging messages.
+	  
+	  Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages.
+	  So, say Y only if you are ready to be annoyed.
 
 config SND_PCM_XRUN_DEBUG
 	bool "Enable PCM ring buffer overrun/underrun debugging"
@@ -184,4 +174,3 @@
 
 config SND_VMASTER
 	bool
-	depends on SND
diff --git a/sound/core/control.c b/sound/core/control.c
index 01a1a5a..281b2e2 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -684,7 +684,8 @@
 	return result;
 }
 
-int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_read(struct snd_card *card,
+			     struct snd_ctl_elem_value *control)
 {
 	struct snd_kcontrol *kctl;
 	struct snd_kcontrol_volatile *vd;
@@ -734,8 +735,8 @@
 	return result;
 }
 
-int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
-		       struct snd_ctl_elem_value *control)
+static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
+			      struct snd_ctl_elem_value *control)
 {
 	struct snd_kcontrol *kctl;
 	struct snd_kcontrol_volatile *vd;
diff --git a/sound/core/init.c b/sound/core/init.c
index ac05734..5c254d4 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -46,17 +46,24 @@
 module_param_array(slots, charp, NULL, 0444);
 MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
 
-/* return non-zero if the given index is already reserved for another
+/* return non-zero if the given index is reserved for the given
  * module via slots option
  */
-static int module_slot_mismatch(struct module *module, int idx)
+static int module_slot_match(struct module *module, int idx)
 {
+	int match = 1;
 #ifdef MODULE
-	char *s1, *s2;
+	const char *s1, *s2;
+
 	if (!module || !module->name || !slots[idx])
 		return 0;
-	s1 = slots[idx];
-	s2 = module->name;
+
+	s1 = module->name;
+	s2 = slots[idx];
+	if (*s2 == '!') {
+		match = 0; /* negative match */
+		s2++;
+	}
 	/* compare module name strings
 	 * hyphens are handled as equivalent with underscore
 	 */
@@ -68,12 +75,12 @@
 		if (c2 == '-')
 			c2 = '_';
 		if (c1 != c2)
-			return 1;
+			return !match;
 		if (!c1)
 			break;
 	}
-#endif
-	return 0;
+#endif /* MODULE */
+	return match;
 }
 
 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
@@ -129,7 +136,7 @@
 			 struct module *module, int extra_size)
 {
 	struct snd_card *card;
-	int err;
+	int err, idx2;
 
 	if (extra_size < 0)
 		extra_size = 0;
@@ -144,35 +151,41 @@
 	err = 0;
 	mutex_lock(&snd_card_mutex);
 	if (idx < 0) {
-		int idx2;
 		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
 			/* idx == -1 == 0xffff means: take any free slot */
 			if (~snd_cards_lock & idx & 1<<idx2) {
-				if (module_slot_mismatch(module, idx2))
-					continue;
-				idx = idx2;
-				if (idx >= snd_ecards_limit)
-					snd_ecards_limit = idx + 1;
-				break;
+				if (module_slot_match(module, idx2)) {
+					idx = idx2;
+					break;
+				}
 			}
-	} else {
-		 if (idx < snd_ecards_limit) {
-			if (snd_cards_lock & (1 << idx))
-				err = -EBUSY;	/* invalid */
-		} else {
-			if (idx < SNDRV_CARDS)
-				snd_ecards_limit = idx + 1; /* increase the limit */
-			else
-				err = -ENODEV;
-		}
 	}
-	if (idx < 0 || err < 0) {
+	if (idx < 0) {
+		for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
+			/* idx == -1 == 0xffff means: take any free slot */
+			if (~snd_cards_lock & idx & 1<<idx2) {
+				if (!slots[idx2] || !*slots[idx2]) {
+					idx = idx2;
+					break;
+				}
+			}
+	}
+	if (idx < 0)
+		err = -ENODEV;
+	else if (idx < snd_ecards_limit) {
+		if (snd_cards_lock & (1 << idx))
+			err = -EBUSY;	/* invalid */
+	} else if (idx >= SNDRV_CARDS)
+		err = -ENODEV;
+	if (err < 0) {
 		mutex_unlock(&snd_card_mutex);
 		snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",
 			 idx, snd_ecards_limit - 1, err);
 		goto __error;
 	}
 	snd_cards_lock |= 1 << idx;		/* lock it */
+	if (idx >= snd_ecards_limit)
+		snd_ecards_limit = idx + 1; /* increase the limit */
 	mutex_unlock(&snd_card_mutex);
 	card->number = idx;
 	card->module = module;
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 23b7bc0..f5d6d8d 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -80,68 +80,6 @@
 #endif
 
 /*
- *  Hacks
- */
-
-#if defined(__i386__)
-/*
- * A hack to allocate large buffers via dma_alloc_coherent()
- *
- * since dma_alloc_coherent always tries GFP_DMA when the requested
- * pci memory region is below 32bit, it happens quite often that even
- * 2 order of pages cannot be allocated.
- *
- * so in the following, we allocate at first without dma_mask, so that
- * allocation will be done without GFP_DMA.  if the area doesn't match
- * with the requested region, then realloate with the original dma_mask
- * again.
- *
- * Really, we want to move this type of thing into dma_alloc_coherent()
- * so dma_mask doesn't have to be messed with.
- */
-
-static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
-					 dma_addr_t *dma_handle,
-					 gfp_t flags)
-{
-	void *ret;
-	u64 dma_mask, coherent_dma_mask;
-
-	if (dev == NULL || !dev->dma_mask)
-		return dma_alloc_coherent(dev, size, dma_handle, flags);
-	dma_mask = *dev->dma_mask;
-	coherent_dma_mask = dev->coherent_dma_mask;
-	*dev->dma_mask = 0xffffffff; 	/* do without masking */
-	dev->coherent_dma_mask = 0xffffffff; 	/* do without masking */
-	ret = dma_alloc_coherent(dev, size, dma_handle, flags);
-	*dev->dma_mask = dma_mask;	/* restore */
-	dev->coherent_dma_mask = coherent_dma_mask;	/* restore */
-	if (ret) {
-		/* obtained address is out of range? */
-		if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) {
-			/* reallocate with the proper mask */
-			dma_free_coherent(dev, size, ret, *dma_handle);
-			ret = dma_alloc_coherent(dev, size, dma_handle, flags);
-		}
-	} else {
-		/* wish to success now with the proper mask... */
-		if (dma_mask != 0xffffffffUL) {
-			/* allocation with GFP_ATOMIC to avoid the long stall */
-			flags &= ~GFP_KERNEL;
-			flags |= GFP_ATOMIC;
-			ret = dma_alloc_coherent(dev, size, dma_handle, flags);
-		}
-	}
-	return ret;
-}
-
-/* redefine dma_alloc_coherent for some architectures */
-#undef dma_alloc_coherent
-#define dma_alloc_coherent snd_dma_hack_alloc_coherent
-
-#endif /* arch */
-
-/*
  *
  *  Generic memory allocators
  *
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 47cfa51..7a1545d 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -148,7 +148,7 @@
 		return NULL;
 	}
 	spin_unlock_irqrestore(&clients_lock, flags);
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 	if (!in_interrupt()) {
 		static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS];
 		static char card_requested[SNDRV_CARDS];
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 2f00ad2..05410e5 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -124,7 +124,7 @@
  * load all registered drivers (called from seq_clientmgr.c)
  */
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 /* avoid auto-loading during module_init() */
 static int snd_seq_in_init;
 void snd_seq_autoload_lock(void)
@@ -140,7 +140,7 @@
 
 void snd_seq_device_load_drivers(void)
 {
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 	struct ops_list *ops;
 
 	/* Calling request_module during module_init()
@@ -566,7 +566,5 @@
 EXPORT_SYMBOL(snd_seq_device_new);
 EXPORT_SYMBOL(snd_seq_device_register_driver);
 EXPORT_SYMBOL(snd_seq_device_unregister_driver);
-#ifdef CONFIG_KMOD
 EXPORT_SYMBOL(snd_seq_autoload_lock);
 EXPORT_SYMBOL(snd_seq_autoload_unlock);
-#endif
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 6c8ab48..09a9495 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -60,14 +60,14 @@
 static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
 static DEFINE_MUTEX(sound_mutex);
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 
 /**
  * snd_request_card - try to load the card module
  * @card: the card number
  *
  * Tries to load the module "snd-card-X" for the given card number
- * via KMOD.  Returns immediately if already loaded.
+ * via request_module.  Returns immediately if already loaded.
  */
 void snd_request_card(int card)
 {
@@ -92,7 +92,7 @@
 	request_module(str);
 }
 
-#endif				/* request_module support */
+#endif	/* modular kernel */
 
 /**
  * snd_lookup_minor_data - get user data of a registered device
@@ -132,7 +132,7 @@
 		return -ENODEV;
 	mptr = snd_minors[minor];
 	if (mptr == NULL) {
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 		int dev = SNDRV_MINOR_DEVICE(minor);
 		if (dev == SNDRV_MINOR_CONTROL) {
 			/* /dev/aloadC? */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 9d8184a..0af337e 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -146,7 +146,7 @@
 	return NULL;
 }
 
-#ifdef CONFIG_KMOD
+#ifdef CONFIG_MODULES
 
 static void snd_timer_request(struct snd_timer_id *tid)
 {
@@ -259,8 +259,8 @@
 	/* open a master instance */
 	mutex_lock(&register_mutex);
 	timer = snd_timer_find(tid);
-#ifdef CONFIG_KMOD
-	if (timer == NULL) {
+#ifdef CONFIG_MODULES
+	if (!timer) {
 		mutex_unlock(&register_mutex);
 		snd_timer_request(tid);
 		mutex_lock(&register_mutex);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 602b58e..255fd18 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -1,15 +1,41 @@
-# ALSA generic drivers
+config SND_MPU401_UART
+        tristate
+        select SND_RAWMIDI
 
-menu "Generic devices"
-	depends on SND!=n
+config SND_OPL3_LIB
+	tristate
+	select SND_TIMER
+	select SND_HWDEP
 
+config SND_OPL4_LIB
+	tristate
+	select SND_TIMER
+	select SND_HWDEP
+
+config SND_VX_LIB
+	tristate
+	select SND_HWDEP
+	select SND_PCM
+
+config SND_AC97_CODEC
+	tristate
+	select SND_PCM
+	select AC97_BUS
+	select SND_VMASTER
+
+menuconfig SND_DRIVERS
+	bool "Generic sound devices"
+	default y
+	help
+	  Support for generic sound devices.
+  
+if SND_DRIVERS
 
 config SND_PCSP
 	tristate "PC-Speaker support (READ HELP!)"
 	depends on PCSPKR_PLATFORM && X86_PC && HIGH_RES_TIMERS
 	depends on INPUT
 	depends on EXPERIMENTAL
-	depends on SND
 	select SND_PCM
 	help
 	  If you don't have a sound card in your computer, you can include a
@@ -35,33 +61,8 @@
 	  Say M if you don't.
 	  Say Y only if you really know what you do.
 
-config SND_MPU401_UART
-        tristate
-        select SND_RAWMIDI
-
-config SND_OPL3_LIB
-	tristate
-	select SND_TIMER
-	select SND_HWDEP
-
-config SND_OPL4_LIB
-	tristate
-	select SND_TIMER
-	select SND_HWDEP
-
-config SND_VX_LIB
-	tristate
-	select SND_HWDEP
-	select SND_PCM
-
-config SND_AC97_CODEC
-	tristate
-	select SND_PCM
-	select AC97_BUS
-
 config SND_DUMMY
 	tristate "Dummy (/dev/null) soundcard"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include the dummy driver.  This driver does
@@ -90,7 +91,6 @@
 
 config SND_MTPAV
 	tristate "MOTU MidiTimePiece AV multiport MIDI"
-	depends on SND
 	select SND_RAWMIDI
 	help
 	  To use a MOTU MidiTimePiece AV multiport MIDI adapter
@@ -102,7 +102,7 @@
 
 config SND_MTS64
 	tristate "ESI Miditerminal 4140 driver"
-	depends on SND && PARPORT
+	depends on PARPORT
 	select SND_RAWMIDI
 	help
 	  The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with 
@@ -115,7 +115,6 @@
 
 config SND_SERIAL_U16550
 	tristate "UART16550 serial MIDI driver"
-	depends on SND
 	select SND_RAWMIDI
 	help
 	  To include support for MIDI serial port interfaces, say Y here
@@ -131,7 +130,6 @@
 
 config SND_MPU401
 	tristate "Generic MPU-401 UART driver"
-	depends on SND
 	select SND_MPU401_UART
 	help
 	  Say Y here to include support for MIDI ports compatible with
@@ -142,7 +140,7 @@
 
 config SND_PORTMAN2X4
 	tristate "Portman 2x4 driver"
-	depends on SND && PARPORT
+	depends on PARPORT
 	select SND_RAWMIDI
 	help
 	  Say Y here to include support for Midiman Portman 2x4 parallel
@@ -153,7 +151,7 @@
 
 config SND_ML403_AC97CR
 	tristate "Xilinx ML403 AC97 Controller Reference"
-	depends on SND && XILINX_VIRTEX
+	depends on XILINX_VIRTEX
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the
@@ -163,4 +161,25 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-ml403_ac97cr.
 
-endmenu
+config SND_AC97_POWER_SAVE
+	bool "AC97 Power-Saving Mode"
+	depends on SND_AC97_CODEC && EXPERIMENTAL
+	default n
+	help
+	  Say Y here to enable the aggressive power-saving support of
+	  AC97 codecs.  In this mode, the power-mode is dynamically
+	  controlled at each open/close.
+
+	  The mode is activated by passing power_save=1 option to
+	  snd-ac97-codec driver.  You can toggle it dynamically over
+	  sysfs, too.
+
+config SND_AC97_POWER_SAVE_DEFAULT
+	int "Default time-out for AC97 power-save mode"
+	depends on SND_AC97_POWER_SAVE
+	default 0
+	help
+	  The default time-out value in seconds for AC97 automatic
+	  power-save mode.  0 means to disable the power-save mode.
+
+endif	# SND_DRIVERS
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index 1dfe694..efd22e9 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -183,7 +183,7 @@
 		kfree(fw);
 		return -ENOMEM;
 	}
-	if (copy_from_user(fw->data, dsp->image, dsp->length)) {
+	if (copy_from_user((void *)fw->data, dsp->image, dsp->length)) {
 		free_fw(fw);
 		return -EFAULT;
 	}
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index e57e9cb..9c3d361 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <asm/unaligned.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
@@ -264,10 +265,7 @@
 		goto __fail;
 	}
 	/* write default channel status bytes */
-	buf[0] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0));
-	buf[1] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8));
-	buf[2] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16));
-	buf[3] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24));
+	put_unaligned_le32(SNDRV_PCM_DEFAULT_CON_SPDIF, buf);
 	memset(buf + 4, 0, 24 - 4);
 	if (snd_cs8427_send_corudata(device, 0, buf, 24) < 0)
 		goto __fail;
diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
index bfa5d2c..1f4942e 100644
--- a/sound/i2c/l3/uda1341.c
+++ b/sound/i2c/l3/uda1341.c
@@ -17,8 +17,6 @@
  * 2002-05-12   Tomas Kasparek  another code cleanup
  */
 
-/* $Id: uda1341.c,v 1.18 2005/11/17 14:17:21 tiwai Exp $ */
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 2639a6a..25347a2 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -21,12 +21,17 @@
         select SND_PCM
         select SND_SB_COMMON
 
-menu "ISA devices"
-	depends on SND!=n && ISA && ISA_DMA_API
+menuconfig SND_ISA
+	bool "ISA sound devices"
+	depends on ISA && ISA_DMA_API
+	default y
+	help
+	  Support for sound devices connected via the ISA bus.
+
+if SND_ISA
 
 config SND_ADLIB
 	tristate "AdLib FM card"
-	depends on SND
 	select SND_OPL3_LIB
 	help
 	  Say Y here to include support for AdLib FM cards.
@@ -36,7 +41,7 @@
 
 config SND_AD1816A
 	tristate "Analog Devices SoundPort AD1816A"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -50,7 +55,6 @@
 
 config SND_AD1848
 	tristate "Generic AD1848/CS4248 driver"
-	depends on SND
 	select SND_AD1848_LIB
 	help
 	  Say Y here to include support for AD1848 (Analog Devices) or
@@ -64,7 +68,7 @@
 
 config SND_ALS100
 	tristate "Avance Logic ALS100/ALS120"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -78,7 +82,7 @@
 
 config SND_AZT2320
 	tristate "Aztech Systems AZT2320"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -92,7 +96,6 @@
 
 config SND_CMI8330
 	tristate "C-Media CMI8330"
-	depends on SND
 	select SND_AD1848_LIB
 	select SND_SB16_DSP
 	help
@@ -104,7 +107,6 @@
 
 config SND_CS4231
 	tristate "Generic Cirrus Logic CS4231 driver"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
 	help
@@ -116,7 +118,6 @@
 
 config SND_CS4232
 	tristate "Generic Cirrus Logic CS4232 driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -129,7 +130,6 @@
 
 config SND_CS4236
 	tristate "Generic Cirrus Logic CS4236+ driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -142,7 +142,7 @@
 
 config SND_DT019X
 	tristate "Diamond Technologies DT-019X, Avance Logic ALS-007"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -156,7 +156,7 @@
 
 config SND_ES968
 	tristate "Generic ESS ES968 driver"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select ISAPNP
 	select SND_MPU401_UART
 	select SND_SB8_DSP
@@ -168,7 +168,6 @@
 
 config SND_ES1688
 	tristate "Generic ESS ES688/ES1688 driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -181,7 +180,6 @@
 
 config SND_ES18XX
 	tristate "Generic ESS ES18xx driver"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -193,7 +191,7 @@
 
 config SND_SC6000
 	tristate "Gallant SC-6000, Audio Excel DSP 16"
-	depends on SND && HAS_IOPORT
+	depends on HAS_IOPORT
 	select SND_AD1848_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -204,15 +202,10 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-sc6000.
 
-config SND_GUS_SYNTH
-	tristate
-
 config SND_GUSCLASSIC
 	tristate "Gravis UltraSound Classic"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_PCM
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for Gravis UltraSound Classic
 	  soundcards.
@@ -222,11 +215,9 @@
 
 config SND_GUSEXTREME
 	tristate "Gravis UltraSound Extreme"
-	depends on SND
 	select SND_HWDEP
 	select SND_MPU401_UART
 	select SND_PCM
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for Gravis UltraSound Extreme
 	  soundcards.
@@ -236,10 +227,8 @@
 
 config SND_GUSMAX
 	tristate "Gravis UltraSound MAX"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_CS4231_LIB
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for Gravis UltraSound MAX
 	  soundcards.
@@ -249,10 +238,9 @@
 
 config SND_INTERWAVE
 	tristate "AMD InterWave, Gravis UltraSound PnP"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select SND_RAWMIDI
 	select SND_CS4231_LIB
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for AMD InterWave based
 	  soundcards (Gravis UltraSound Plug & Play, STB SoundRage32,
@@ -263,10 +251,9 @@
 
 config SND_INTERWAVE_STB
 	tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)"
-	depends on SND && PNP && ISA
+	depends on PNP
 	select SND_RAWMIDI
 	select SND_CS4231_LIB
-	select SND_GUS_SYNTH
 	help
 	  Say Y here to include support for AMD InterWave based
 	  soundcards with a TEA6330T bass and treble regulator
@@ -277,7 +264,6 @@
 
 config SND_OPL3SA2
 	tristate "Yamaha OPL3-SA2/SA3"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -290,7 +276,6 @@
 
 config SND_OPTI92X_AD1848
 	tristate "OPTi 82C92x - AD1848"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_OPL4_LIB
 	select SND_MPU401_UART
@@ -304,7 +289,6 @@
 
 config SND_OPTI92X_CS4231
 	tristate "OPTi 82C92x - CS4231"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_OPL4_LIB
 	select SND_MPU401_UART
@@ -318,10 +302,9 @@
 
 config SND_OPTI93X
 	tristate "OPTi 82C93x"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_CS4231_LIB
 	help
 	  Say Y here to include support for soundcards based on Opti
 	  82C93x chips.
@@ -331,7 +314,6 @@
 
 config SND_MIRO
 	tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"
-	depends on SND
 	select SND_OPL4_LIB
 	select SND_CS4231_LIB
 	select SND_MPU401_UART
@@ -345,7 +327,6 @@
 
 config SND_SB8
 	tristate "Sound Blaster 1.0/2.0/Pro (8-bit)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_RAWMIDI
 	select SND_SB8_DSP
@@ -358,7 +339,6 @@
 
 config SND_SB16
 	tristate "Sound Blaster 16 (PnP)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_SB16_DSP
@@ -371,7 +351,6 @@
 
 config SND_SBAWE
 	tristate "Sound Blaster AWE (32,64) (PnP)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_SB16_DSP
@@ -402,7 +381,6 @@
 
 config SND_SGALAXY
 	tristate "Aztech Sound Galaxy"
-	depends on SND
 	select SND_AD1848_LIB
 	help
 	  Say Y here to include support for Aztech Sound Galaxy
@@ -413,7 +391,6 @@
 
 config SND_SSCAPE
 	tristate "Ensoniq SoundScape PnP driver"
-	depends on SND
 	select SND_HWDEP
 	select SND_MPU401_UART
 	select SND_CS4231_LIB
@@ -426,7 +403,6 @@
 
 config SND_WAVEFRONT
 	tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)"
-	depends on SND
 	select FW_LOADER
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -448,4 +424,5 @@
 	  you need to install the firmware files from the
 	  alsa-firmware package.
 
-endmenu
+endif	# SND_ISA
+
diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
index 0aa8649..521db70 100644
--- a/sound/isa/cs423x/cs4231_lib.c
+++ b/sound/isa/cs423x/cs4231_lib.c
@@ -119,6 +119,42 @@
 	0x00,			/* 1f/31 - cbrl */
 };
 
+static unsigned char snd_opti93x_original_image[32] =
+{
+	0x00,		/* 00/00 - l_mixout_outctrl */
+	0x00,		/* 01/01 - r_mixout_outctrl */
+	0x88,		/* 02/02 - l_cd_inctrl */
+	0x88,		/* 03/03 - r_cd_inctrl */
+	0x88,		/* 04/04 - l_a1/fm_inctrl */
+	0x88,		/* 05/05 - r_a1/fm_inctrl */
+	0x80,		/* 06/06 - l_dac_inctrl */
+	0x80,		/* 07/07 - r_dac_inctrl */
+	0x00,		/* 08/08 - ply_dataform_reg */
+	0x00,		/* 09/09 - if_conf */
+	0x00,		/* 0a/10 - pin_ctrl */
+	0x00,		/* 0b/11 - err_init_reg */
+	0x0a,		/* 0c/12 - id_reg */
+	0x00,		/* 0d/13 - reserved */
+	0x00,		/* 0e/14 - ply_upcount_reg */
+	0x00,		/* 0f/15 - ply_lowcount_reg */
+	0x88,		/* 10/16 - reserved/l_a1_inctrl */
+	0x88,		/* 11/17 - reserved/r_a1_inctrl */
+	0x88,		/* 12/18 - l_line_inctrl */
+	0x88,		/* 13/19 - r_line_inctrl */
+	0x88,		/* 14/20 - l_mic_inctrl */
+	0x88,		/* 15/21 - r_mic_inctrl */
+	0x80,		/* 16/22 - l_out_outctrl */
+	0x80,		/* 17/23 - r_out_outctrl */
+	0x00,		/* 18/24 - reserved */
+	0x00,		/* 19/25 - reserved */
+	0x00,		/* 1a/26 - reserved */
+	0x00,		/* 1b/27 - reserved */
+	0x00,		/* 1c/28 - cap_dataform_reg */
+	0x00,		/* 1d/29 - reserved */
+	0x00,		/* 1e/30 - cap_upcount_reg */
+	0x00		/* 1f/31 - cap_lowcount_reg */
+};
+
 /*
  *  Basic I/O functions
  */
@@ -895,7 +931,7 @@
 	return 0;
 }
 
-static void snd_cs4231_overrange(struct snd_cs4231 *chip)
+void snd_cs4231_overrange(struct snd_cs4231 *chip)
 {
 	unsigned long flags;
 	unsigned char res;
@@ -1054,8 +1090,11 @@
 	chip->image[CS4231_IFACE_CTRL] =
 	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
 	    (chip->single_dma ? CS4231_SINGLE_DMA : 0);
-	chip->image[CS4231_ALT_FEATURE_1] = 0x80;
-	chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
+	if (chip->hardware != CS4231_HW_OPTI93X) {
+		chip->image[CS4231_ALT_FEATURE_1] = 0x80;
+		chip->image[CS4231_ALT_FEATURE_2] =
+			chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01;
+	}
 	ptr = (unsigned char *) &chip->image;
 	snd_cs4231_mce_down(chip);
 	spin_lock_irqsave(&chip->reg_lock, flags);
@@ -1376,6 +1415,7 @@
 	case CS4231_HW_INTERWAVE: return "AMD InterWave";
 	case CS4231_HW_OPL3SA2: return chip->card->shortname;
 	case CS4231_HW_AD1845: return "AD1845";
+	case CS4231_HW_OPTI93X: return "OPTi 93x";
 	default: return "???";
 	}
 }
@@ -1401,8 +1441,13 @@
 	chip->rate_constraint = snd_cs4231_xrate;
 	chip->set_playback_format = snd_cs4231_playback_format;
 	chip->set_capture_format = snd_cs4231_capture_format;
-        memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image));
-        
+	if (chip->hardware == CS4231_HW_OPTI93X)
+		memcpy(&chip->image, &snd_opti93x_original_image,
+		       sizeof(snd_opti93x_original_image));
+	else
+		memcpy(&chip->image, &snd_cs4231_original_image,
+		       sizeof(snd_cs4231_original_image));
+
         *rchip = chip;
         return 0;
 }
@@ -1790,6 +1835,48 @@
 CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1)
 };
                                         
+static struct snd_kcontrol_new snd_opti93x_controls[] = {
+CS4231_DOUBLE("Master Playback Switch", 0,
+	      OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
+CS4231_DOUBLE("Master Playback Volume", 0,
+	      OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
+CS4231_DOUBLE("PCM Playback Switch", 0,
+	      CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("PCM Playback Volume", 0,
+	      CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1),
+CS4231_DOUBLE("FM Playback Switch", 0,
+	      CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("FM Playback Volume", 0,
+	      CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1),
+CS4231_DOUBLE("Line Playback Switch", 0,
+	      CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+CS4231_DOUBLE("Line Playback Volume", 0,
+	      CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1),
+CS4231_DOUBLE("Mic Playback Switch", 0,
+	      OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Mic Playback Volume", 0,
+	      OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
+CS4231_DOUBLE("Mic Boost", 0,
+	      CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
+CS4231_DOUBLE("CD Playback Switch", 0,
+	      CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("CD Playback Volume", 0,
+	      CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1),
+CS4231_DOUBLE("Aux Playback Switch", 0,
+	      OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
+CS4231_DOUBLE("Aux Playback Volume", 0,
+	      OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
+CS4231_DOUBLE("Capture Volume", 0,
+	      CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_cs4231_info_mux,
+	.get = snd_cs4231_get_mux,
+	.put = snd_cs4231_put_mux,
+}
+};
+
 int snd_cs4231_mixer(struct snd_cs4231 *chip)
 {
 	struct snd_card *card;
@@ -1802,10 +1889,22 @@
 
 	strcpy(card->mixername, chip->pcm->name);
 
-	for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
-		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0)
-			return err;
-	}
+	if (chip->hardware == CS4231_HW_OPTI93X)
+		for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
+			err = snd_ctl_add(card,
+					snd_ctl_new1(&snd_opti93x_controls[idx],
+						     chip));
+			if (err < 0)
+				return err;
+		}
+	else
+		for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
+			err = snd_ctl_add(card,
+					snd_ctl_new1(&snd_cs4231_controls[idx],
+						     chip));
+			if (err < 0)
+				return err;
+		}
 	return 0;
 }
 
@@ -1815,6 +1914,7 @@
 EXPORT_SYMBOL(snd_cs4236_ext_in);
 EXPORT_SYMBOL(snd_cs4231_mce_up);
 EXPORT_SYMBOL(snd_cs4231_mce_down);
+EXPORT_SYMBOL(snd_cs4231_overrange);
 EXPORT_SYMBOL(snd_cs4231_interrupt);
 EXPORT_SYMBOL(snd_cs4231_chip_id);
 EXPORT_SYMBOL(snd_cs4231_create);
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index fe1afc1..41c047e 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -33,15 +33,10 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <sound/core.h>
-#ifdef CS4231
+#if defined(CS4231) || defined(OPTi93X)
 #include <sound/cs4231.h>
 #else
-#ifndef OPTi93X
 #include <sound/ad1848.h>
-#else
-#include <sound/control.h>
-#include <sound/pcm.h>
-#endif	/* OPTi93X */
 #endif	/* CS4231 */
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
@@ -109,7 +104,6 @@
 MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
 #endif	/* CS4231 || OPTi93X */
 
-#define OPTi9XX_HW_DETECT	0
 #define OPTi9XX_HW_82C928	1
 #define OPTi9XX_HW_82C929	2
 #define OPTi9XX_HW_82C924	3
@@ -123,105 +117,12 @@
 
 #ifdef OPTi93X
 
-#define OPTi93X_INDEX			0x00
-#define OPTi93X_DATA			0x01
 #define OPTi93X_STATUS			0x02
-#define OPTi93X_DDATA			0x03
 #define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
 
-#define OPTi93X_MIXOUT_LEFT		0x00
-#define OPTi93X_MIXOUT_RIGHT		0x01
-#define OPTi93X_CD_LEFT_INPUT		0x02
-#define OPTi93X_CD_RIGHT_INPUT		0x03
-#define OPTi930_AUX_LEFT_INPUT		0x04
-#define OPTi930_AUX_RIGHT_INPUT		0x05
-#define OPTi931_FM_LEFT_INPUT		0x04
-#define OPTi931_FM_RIGHT_INPUT		0x05
-#define OPTi93X_DAC_LEFT		0x06
-#define OPTi93X_DAC_RIGHT		0x07
-#define OPTi93X_PLAY_FORMAT		0x08
-#define OPTi93X_IFACE_CONF		0x09
-#define OPTi93X_PIN_CTRL		0x0a
-#define OPTi93X_ERR_INIT		0x0b
-#define OPTi93X_ID			0x0c
-#define OPTi93X_PLAY_UPR_CNT		0x0e
-#define OPTi93X_PLAY_LWR_CNT		0x0f
-#define OPTi931_AUX_LEFT_INPUT		0x10
-#define OPTi931_AUX_RIGHT_INPUT		0x11
-#define OPTi93X_LINE_LEFT_INPUT		0x12
-#define OPTi93X_LINE_RIGHT_INPUT	0x13
-#define OPTi93X_MIC_LEFT_INPUT		0x14
-#define OPTi93X_MIC_RIGHT_INPUT		0x15
-#define OPTi93X_OUT_LEFT		0x16
-#define OPTi93X_OUT_RIGHT		0x17
-#define OPTi93X_CAPT_FORMAT		0x1c
-#define OPTi93X_CAPT_UPR_CNT		0x1e
-#define OPTi93X_CAPT_LWR_CNT		0x1f
-
-#define OPTi93X_TRD			0x20
-#define OPTi93X_MCE			0x40
-#define OPTi93X_INIT			0x80
-
-#define OPTi93X_MIXOUT_MIC_GAIN		0x20
-#define OPTi93X_MIXOUT_LINE		0x00
-#define OPTi93X_MIXOUT_CD		0x40
-#define OPTi93X_MIXOUT_MIC		0x80
-#define OPTi93X_MIXOUT_MIXER		0xc0
-
-#define OPTi93X_STEREO			0x10
-#define OPTi93X_LINEAR_8		0x00
-#define OPTi93X_ULAW_8			0x20
-#define OPTi93X_LINEAR_16_LIT		0x40
-#define OPTi93X_ALAW_8			0x60
-#define OPTi93X_ADPCM_16		0xa0
-#define OPTi93X_LINEAR_16_BIG		0xc0
-
-#define OPTi93X_CAPTURE_PIO		0x80
-#define OPTi93X_PLAYBACK_PIO		0x40
-#define OPTi93X_AUTOCALIB		0x08
-#define OPTi93X_SINGLE_DMA		0x04
-#define OPTi93X_CAPTURE_ENABLE		0x02
-#define OPTi93X_PLAYBACK_ENABLE		0x01
-
-#define OPTi93X_IRQ_ENABLE		0x02
-
-#define OPTi93X_DMA_REQUEST		0x10
-#define OPTi93X_CALIB_IN_PROGRESS	0x20
-
 #define OPTi93X_IRQ_PLAYBACK		0x04
 #define OPTi93X_IRQ_CAPTURE		0x08
 
-
-struct snd_opti93x {
-	unsigned long port;
-	struct resource *res_port;
-	int irq;
-	int dma1;
-	int dma2;
-
-	struct snd_opti9xx *chip;
-	unsigned short hardware;
-	unsigned char image[32];
-
-	unsigned char mce_bit;
-	unsigned short mode;
-	int mute;
-
-	spinlock_t lock;
-
-	struct snd_card *card;
-	struct snd_pcm *pcm;
-	struct snd_pcm_substream *playback_substream;
-	struct snd_pcm_substream *capture_substream;
-	unsigned int p_dma_size;
-	unsigned int c_dma_size;
-};
-
-#define OPTi93X_MODE_NONE	0x00
-#define OPTi93X_MODE_PLAY	0x01
-#define OPTi93X_MODE_CAPTURE	0x02
-#define OPTi93X_MODE_OPEN	(OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE)
-
 #endif /* OPTi93X */
 
 struct snd_opti9xx {
@@ -234,6 +135,7 @@
 	unsigned long mc_base_size;
 #ifdef OPTi93X
 	unsigned long mc_indir_index;
+	struct snd_cs4231 *codec;
 #endif	/* OPTi93X */
 	unsigned long pwd_reg;
 
@@ -491,16 +393,9 @@
 		break;
 
 #else	/* OPTi93X */
-	case OPTi9XX_HW_82C930:
 	case OPTi9XX_HW_82C931:
 	case OPTi9XX_HW_82C933:
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
-			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
-			0x34);
-		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
-		/* 
+		/*
 		 * The BTC 1817DW has QS1000 wavetable which is connected
 		 * to the serial digital input of the OPTI931.
 		 */
@@ -510,6 +405,13 @@
 		 * or digital input signal.
 		 */
 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
+	case OPTi9XX_HW_82C930: /* FALL THROUGH */
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
+			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
+			0x34);
+		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
 		break;
 #endif	/* OPTi93X */
 
@@ -654,979 +556,23 @@
 
 #ifdef OPTi93X
 
-static unsigned char snd_opti93x_default_image[32] =
-{
-	0x00,		/* 00/00 - l_mixout_outctrl */
-	0x00,		/* 01/01 - r_mixout_outctrl */
-	0x88,		/* 02/02 - l_cd_inctrl */
-	0x88,		/* 03/03 - r_cd_inctrl */
-	0x88,		/* 04/04 - l_a1/fm_inctrl */
-	0x88,		/* 05/05 - r_a1/fm_inctrl */
-	0x80,		/* 06/06 - l_dac_inctrl */
-	0x80,		/* 07/07 - r_dac_inctrl */
-	0x00,		/* 08/08 - ply_dataform_reg */
-	0x00,		/* 09/09 - if_conf */
-	0x00,		/* 0a/10 - pin_ctrl */
-	0x00,		/* 0b/11 - err_init_reg */
-	0x0a,		/* 0c/12 - id_reg */
-	0x00,		/* 0d/13 - reserved */
-	0x00,		/* 0e/14 - ply_upcount_reg */
-	0x00,		/* 0f/15 - ply_lowcount_reg */
-	0x88,		/* 10/16 - reserved/l_a1_inctrl */
-	0x88,		/* 11/17 - reserved/r_a1_inctrl */
-	0x88,		/* 12/18 - l_line_inctrl */
-	0x88,		/* 13/19 - r_line_inctrl */
-	0x88,		/* 14/20 - l_mic_inctrl */
-	0x88,		/* 15/21 - r_mic_inctrl */
-	0x80,		/* 16/22 - l_out_outctrl */
-	0x80,		/* 17/23 - r_out_outctrl */
-	0x00,		/* 18/24 - reserved */
-	0x00,		/* 19/25 - reserved */
-	0x00,		/* 1a/26 - reserved */
-	0x00,		/* 1b/27 - reserved */
-	0x00,		/* 1c/28 - cap_dataform_reg */
-	0x00,		/* 1d/29 - reserved */
-	0x00,		/* 1e/30 - cap_upcount_reg */
-	0x00		/* 1f/31 - cap_lowcount_reg */
-};
-
-
-static int snd_opti93x_busy_wait(struct snd_opti93x *chip)
-{
-	int timeout;
-
-	for (timeout = 250; timeout-- > 0; udelay(10))
-		if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT))
-			return 0;
-
-	snd_printk("chip still busy.\n");
-	return -EBUSY;
-}
-
-static unsigned char snd_opti93x_in(struct snd_opti93x *chip, unsigned char reg)
-{
-	snd_opti93x_busy_wait(chip);
-	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
-	return inb(OPTi93X_PORT(chip, DATA));
-}
-
-static void snd_opti93x_out(struct snd_opti93x *chip, unsigned char reg,
-			    unsigned char value)
-{
-	snd_opti93x_busy_wait(chip);
-	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
-	outb(value, OPTi93X_PORT(chip, DATA));
-}
-
-static void snd_opti93x_out_image(struct snd_opti93x *chip, unsigned char reg,
-				  unsigned char value)
-{
-	snd_opti93x_out(chip, reg, chip->image[reg] = value);
-}
-
-static void snd_opti93x_out_mask(struct snd_opti93x *chip, unsigned char reg,
-				 unsigned char mask, unsigned char value)
-{
-	snd_opti93x_out_image(chip, reg,
-		(chip->image[reg] & ~mask) | (value & mask));
-}
-
-
-static void snd_opti93x_mce_up(struct snd_opti93x *chip)
-{
-	snd_opti93x_busy_wait(chip);
-
-	chip->mce_bit = OPTi93X_MCE;
-	if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE))
-		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
-}
-
-static void snd_opti93x_mce_down(struct snd_opti93x *chip)
-{
-	snd_opti93x_busy_wait(chip);
-
-	chip->mce_bit = 0;
-	if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)
-		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
-}
-
-#define snd_opti93x_mute_reg(chip, reg, mute)	\
-	snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]);
-
-static void snd_opti93x_mute(struct snd_opti93x *chip, int mute)
-{
-	mute = mute ? 1 : 0;
-	if (chip->mute == mute)
-		return;
-
-	chip->mute = mute;
-
-	snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute);
-	switch (chip->hardware) {
-	case OPTi9XX_HW_82C930:
-		snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute);
-		break;
-	case OPTi9XX_HW_82C931:
-	case OPTi9XX_HW_82C933:
-		snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute);
-		snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute);
-	}
-	snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute);
-	snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute);
-}
-
-
-static unsigned int snd_opti93x_get_count(unsigned char format,
-					  unsigned int size)
-{
-	switch (format & 0xe0) {
-	case OPTi93X_LINEAR_16_LIT:
-	case OPTi93X_LINEAR_16_BIG:
-		size >>= 1;
-		break;
-	case OPTi93X_ADPCM_16:
-		return size >> 2;
-	}
-	return (format & OPTi93X_STEREO) ? (size >> 1) : size;
-}
-
-static unsigned int rates[] = {  5512,  6615,  8000,  9600, 11025, 16000, 
-				18900, 22050, 27428, 32000, 33075, 37800,
-				44100, 48000 };
-#define RATES ARRAY_SIZE(rates)
-
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
-	.count = RATES,
-	.list = rates,
-	.mask = 0,
-};
-
-static unsigned char bits[] = {  0x01,  0x0f,  0x00,  0x0e,  0x03,  0x02,
-				 0x05,  0x07,  0x04,  0x06,  0x0d,  0x09,
-				 0x0b,  0x0c};
-
-static unsigned char snd_opti93x_get_freq(unsigned int rate)
-{
-	unsigned int i;
-
-	for (i = 0; i < RATES; i++) {
-		if (rate == rates[i])
-			return bits[i];
-	}
-	snd_BUG();
-	return bits[RATES-1];
-}
-
-static unsigned char snd_opti93x_get_format(struct snd_opti93x *chip,
-					    unsigned int format, int channels)
-{
-	unsigned char retval = OPTi93X_LINEAR_8;
-
-	switch (format) {
-	case SNDRV_PCM_FORMAT_MU_LAW:
-		retval = OPTi93X_ULAW_8;
-		break;
-	case SNDRV_PCM_FORMAT_A_LAW:
-		retval = OPTi93X_ALAW_8;
-		break;
-	case SNDRV_PCM_FORMAT_S16_LE:
-		retval = OPTi93X_LINEAR_16_LIT;
-		break;
-	case SNDRV_PCM_FORMAT_S16_BE:
-		retval = OPTi93X_LINEAR_16_BIG;
-		break;
-	case SNDRV_PCM_FORMAT_IMA_ADPCM:
-		retval = OPTi93X_ADPCM_16;
-	}
-	return (channels > 1) ? (retval | OPTi93X_STEREO) : retval;
-}
-
-
-static void snd_opti93x_playback_format(struct snd_opti93x *chip, unsigned char fmt)
-{
-	unsigned char mask;
-
-	snd_opti93x_mute(chip, 1);
-
-	snd_opti93x_mce_up(chip);
-	mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff;
-	snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt);
-	snd_opti93x_mce_down(chip);
-
-	snd_opti93x_mute(chip, 0);
-}
-
-static void snd_opti93x_capture_format(struct snd_opti93x *chip, unsigned char fmt)
-{
-	snd_opti93x_mute(chip, 1);
-
-	snd_opti93x_mce_up(chip);
-	if (!(chip->mode & OPTi93X_MODE_PLAY))
-		snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt);
-	else
-		fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0;
-	snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt);
-	snd_opti93x_mce_down(chip);
-
-	snd_opti93x_mute(chip, 0);
-}
-
-
-static int snd_opti93x_open(struct snd_opti93x *chip, unsigned int mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	if (chip->mode & mode) {
-		spin_unlock_irqrestore(&chip->lock, flags);
-		return -EAGAIN;
-	}
-
-	if (!(chip->mode & OPTi93X_MODE_OPEN)) {
-		outb(0x00, OPTi93X_PORT(chip, STATUS));
-		snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL,
-			OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE);
-		chip->mode = mode;
-	}
-	else
-		chip->mode |= mode;
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static void snd_opti93x_close(struct snd_opti93x *chip, unsigned int mode)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	chip->mode &= ~mode;
-	if (chip->mode & OPTi93X_MODE_OPEN) {
-		spin_unlock_irqrestore(&chip->lock, flags);
-		return;
-	}
-
-	snd_opti93x_mute(chip, 1);
-
-	outb(0, OPTi93X_PORT(chip, STATUS));
-	snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE,
-		~OPTi93X_IRQ_ENABLE);
-
-	snd_opti93x_mce_up(chip);
-	snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00);
-	snd_opti93x_mce_down(chip);
-	chip->mode = 0;
-
-	snd_opti93x_mute(chip, 0);
-	spin_unlock_irqrestore(&chip->lock, flags);
-}
-
-static int snd_opti93x_trigger(struct snd_pcm_substream *substream, 
-			       unsigned char what, int cmd)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_STOP:
-	{
-		unsigned int what = 0;
-		struct snd_pcm_substream *s;
-		snd_pcm_group_for_each_entry(s, substream) {
-			if (s == chip->playback_substream) {
-				what |= OPTi93X_PLAYBACK_ENABLE;
-				snd_pcm_trigger_done(s, substream);
-			} else if (s == chip->capture_substream) {
-				what |= OPTi93X_CAPTURE_ENABLE;
-				snd_pcm_trigger_done(s, substream);
-			}
-		}
-		spin_lock(&chip->lock);
-		if (cmd == SNDRV_PCM_TRIGGER_START) {
-			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what);
-			if (what & OPTi93X_CAPTURE_ENABLE)
-				udelay(50);
-		} else
-			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00);
-		spin_unlock(&chip->lock);
-		break;
-	}
-	default:
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int snd_opti93x_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	return snd_opti93x_trigger(substream,
-				   OPTi93X_PLAYBACK_ENABLE, cmd);
-}
-
-static int snd_opti93x_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	return snd_opti93x_trigger(substream,
-				   OPTi93X_CAPTURE_ENABLE, cmd);
-}
-
-static int snd_opti93x_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *hw_params)
-{
-	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-
-static int snd_opti93x_hw_free(struct snd_pcm_substream *substream)
-{
-	snd_pcm_lib_free_pages(substream);
-	return 0;
-}
-
-
-static int snd_opti93x_playback_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned char format;
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	chip->p_dma_size = size;
-	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
-		OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO,
-		~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO));
-
-	snd_dma_program(chip->dma1, runtime->dma_addr, size,
-		DMA_MODE_WRITE | DMA_AUTOINIT);
-
-	format = snd_opti93x_get_freq(runtime->rate);
-	format |= snd_opti93x_get_format(chip, runtime->format,
-		runtime->channels);
-	snd_opti93x_playback_format(chip, format);
-	format = chip->image[OPTi93X_PLAY_FORMAT];
-
-	count = snd_opti93x_get_count(format, count) - 1;
-	snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count);
-	snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8);
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static int snd_opti93x_capture_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned long flags;
-	unsigned char format;
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	chip->c_dma_size = size;
-	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
-		OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, 0);
-
-	snd_dma_program(chip->dma2, runtime->dma_addr, size,
-		DMA_MODE_READ | DMA_AUTOINIT);
-
-	format = snd_opti93x_get_freq(runtime->rate);
-	format |= snd_opti93x_get_format(chip, runtime->format,
-		runtime->channels);
-	snd_opti93x_capture_format(chip, format);
-	format = chip->image[OPTi93X_CAPT_FORMAT];
-
-	count = snd_opti93x_get_count(format, count) - 1;
-	snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count);
-	snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8);
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static snd_pcm_uframes_t snd_opti93x_playback_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE))
-		return 0;
-
-	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_opti93x_capture_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-	
-	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE))
-		return 0;
-
-	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-
-static void snd_opti93x_overrange(struct snd_opti93x *chip)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&chip->lock, flags);
-
-	if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02))
-		chip->capture_substream->runtime->overrange++;
-
-	spin_unlock_irqrestore(&chip->lock, flags);
-}
-
 static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
 {
-	struct snd_opti93x *codec = dev_id;
+	struct snd_cs4231 *codec = dev_id;
+	struct snd_opti9xx *chip = codec->card->private_data;
 	unsigned char status;
 
-	status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11));
+	status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
 	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
 		snd_pcm_period_elapsed(codec->playback_substream);
 	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
-		snd_opti93x_overrange(codec);
+		snd_cs4231_overrange(codec);
 		snd_pcm_period_elapsed(codec->capture_substream);
 	}
 	outb(0x00, OPTi93X_PORT(codec, STATUS));
 	return IRQ_HANDLED;
 }
 
-
-static struct snd_pcm_hardware snd_opti93x_playback = {
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
-				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	.rate_min =		5512,
-	.rate_max =		48000,
-	.channels_min =		1,
-	.channels_max =		2,
-	.buffer_bytes_max =	(128*1024),
-	.period_bytes_min =	64,
-	.period_bytes_max =	(128*1024),
-	.periods_min =		1,
-	.periods_max =		1024,
-	.fifo_size =		0,
-};
-
-static struct snd_pcm_hardware snd_opti93x_capture = {
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
-				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	.rates =		SNDRV_PCM_RATE_8000_48000,
-	.rate_min =		5512,
-	.rate_max =		48000,
-	.channels_min =		1,
-	.channels_max =		2,
-	.buffer_bytes_max =	(128*1024),
-	.period_bytes_min =	64,
-	.period_bytes_max =	(128*1024),
-	.periods_min =		1,
-	.periods_max =		1024,
-	.fifo_size =		0,
-};
-
-static int snd_opti93x_playback_open(struct snd_pcm_substream *substream)
-{
-	int error;
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-
-	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0)
-		return error;
-	snd_pcm_set_sync(substream);
-	chip->playback_substream = substream;
-	runtime->hw = snd_opti93x_playback;
-	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-	return error;
-}
-
-static int snd_opti93x_capture_open(struct snd_pcm_substream *substream)
-{
-	int error;
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-
-	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0)
-		return error;
-	runtime->hw = snd_opti93x_capture;
-	snd_pcm_set_sync(substream);
-	chip->capture_substream = substream;
-	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-	return error;
-}
-
-static int snd_opti93x_playback_close(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
-	chip->playback_substream = NULL;
-	snd_opti93x_close(chip, OPTi93X_MODE_PLAY);
-	return 0;
-}
-
-static int snd_opti93x_capture_close(struct snd_pcm_substream *substream)
-{
-	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
-
-	chip->capture_substream = NULL;
-	snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE);
-	return 0;
-}
-
-
-static void snd_opti93x_init(struct snd_opti93x *chip)
-{
-	unsigned long flags;
-	int i;
-
-	spin_lock_irqsave(&chip->lock, flags);
-	snd_opti93x_mce_up(chip);
-
-	for (i = 0; i < 32; i++)
-		snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]);
-
-	snd_opti93x_mce_down(chip);
-	spin_unlock_irqrestore(&chip->lock, flags);
-}
-
-static int snd_opti93x_probe(struct snd_opti93x *chip)
-{
-	unsigned long flags;
-	unsigned char val;
-
-	spin_lock_irqsave(&chip->lock, flags);
-	val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f;
-	spin_unlock_irqrestore(&chip->lock, flags);
-
-	return (val == 0x0a) ? 0 : -ENODEV;
-}
-
-static int snd_opti93x_free(struct snd_opti93x *chip)
-{
-	release_and_free_resource(chip->res_port);
-	if (chip->dma1 >= 0) {
-		disable_dma(chip->dma1);
-		free_dma(chip->dma1);
-	}
-	if (chip->dma2 >= 0) {
-		disable_dma(chip->dma2);
-		free_dma(chip->dma2);
-	}
-	if (chip->irq >= 0) {
-	  free_irq(chip->irq, chip);
-	}
-	kfree(chip);
-	return 0;
-}
-
-static int snd_opti93x_dev_free(struct snd_device *device)
-{
-	struct snd_opti93x *chip = device->device_data;
-	return snd_opti93x_free(chip);
-}
-
-static const char *snd_opti93x_chip_id(struct snd_opti93x *codec)
-{
-	switch (codec->hardware) {
-	case OPTi9XX_HW_82C930: return "82C930";
-	case OPTi9XX_HW_82C931: return "82C931";
-	case OPTi9XX_HW_82C933: return "82C933";
-	default:		return "???";
-	}
-}
-
-static int snd_opti93x_create(struct snd_card *card, struct snd_opti9xx *chip,
-			      int dma1, int dma2,
-			      struct snd_opti93x **rcodec)
-{
-	static struct snd_device_ops ops = {
-		.dev_free =	snd_opti93x_dev_free,
-	};
-	int error;
-	struct snd_opti93x *codec;
-
-	*rcodec = NULL;
-	codec = kzalloc(sizeof(*codec), GFP_KERNEL);
-	if (codec == NULL)
-		return -ENOMEM;
-	codec->irq = -1;
-	codec->dma1 = -1;
-	codec->dma2 = -1;
-
-	if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) {
-		snd_printk(KERN_ERR "opti9xx: can't grab port 0x%lx\n", chip->wss_base + 4);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-	if (request_dma(dma1, "OPTI93x - 1")) {
-		snd_printk(KERN_ERR "opti9xx: can't grab DMA1 %d\n", dma1);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-	codec->dma1 = chip->dma1;
-	if (request_dma(dma2, "OPTI93x - 2")) {
-		snd_printk(KERN_ERR "opti9xx: can't grab DMA2 %d\n", dma2);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-	codec->dma2 = chip->dma2;
-
-	if (request_irq(chip->irq, snd_opti93x_interrupt, IRQF_DISABLED, DEV_NAME" - WSS", codec)) {
-		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
-		snd_opti93x_free(codec);
-		return -EBUSY;
-	}
-
-	codec->card = card;
-	codec->port = chip->wss_base + 4;
-	codec->irq = chip->irq;
-
-	spin_lock_init(&codec->lock);
-	codec->hardware = chip->hardware;
-	codec->chip = chip;
-
-	if ((error = snd_opti93x_probe(codec))) {
-		snd_opti93x_free(codec);
-		return error;
-	}
-
-	snd_opti93x_init(codec);
-
-	/* Register device */
-	if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) {
-		snd_opti93x_free(codec);
-		return error;
-	}
-
-	*rcodec = codec;
-	return 0;
-}
-
-static struct snd_pcm_ops snd_opti93x_playback_ops = {
-	.open =		snd_opti93x_playback_open,
-	.close =	snd_opti93x_playback_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_opti93x_hw_params,
-	.hw_free =	snd_opti93x_hw_free,
-	.prepare =	snd_opti93x_playback_prepare,
-	.trigger =	snd_opti93x_playback_trigger,
-	.pointer =	snd_opti93x_playback_pointer,
-};
-
-static struct snd_pcm_ops snd_opti93x_capture_ops = {
-	.open =		snd_opti93x_capture_open,
-	.close =	snd_opti93x_capture_close,
-	.ioctl =	snd_pcm_lib_ioctl,
-	.hw_params =	snd_opti93x_hw_params,
-	.hw_free =	snd_opti93x_hw_free,
-	.prepare =	snd_opti93x_capture_prepare,
-	.trigger =	snd_opti93x_capture_trigger,
-	.pointer =	snd_opti93x_capture_pointer,
-};
-
-static int snd_opti93x_pcm(struct snd_opti93x *codec, int device, struct snd_pcm **rpcm)
-{
-	int error;
-	struct snd_pcm *pcm;
-
-	if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm)) < 0)
-		return error;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops);
-
-	pcm->private_data = codec;
-	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
-
-	strcpy(pcm->name, snd_opti93x_chip_id(codec));
-
-	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-					      snd_dma_isa_data(),
-					      64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024);
-
-	codec->pcm = pcm;
-	if (rpcm)
-		*rpcm = pcm;
-	return 0;
-}
-
-/*
- *  MIXER part
- */
-
-static int snd_opti93x_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	static char *texts[4] = {
-		"Line1", "Aux", "Mic", "Mix"
-	};
-
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 2;
-	uinfo->value.enumerated.items = 4;
-	if (uinfo->value.enumerated.item > 3)
-		uinfo->value.enumerated.item = 3;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-	return 0;
-}
-
-static int snd_opti93x_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	
-	spin_lock_irqsave(&chip->lock, flags);
-	ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6;
-	ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6;
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return 0;
-}
-
-static int snd_opti93x_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	unsigned short left, right;
-	int change;
-	
-	if (ucontrol->value.enumerated.item[0] > 3 ||
-	    ucontrol->value.enumerated.item[1] > 3)
-		return -EINVAL;
-	left = ucontrol->value.enumerated.item[0] << 6;
-	right = ucontrol->value.enumerated.item[1] << 6;
-	spin_lock_irqsave(&chip->lock, flags);
-	left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left;
-	right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right;
-	change = left != chip->image[OPTi93X_MIXOUT_LEFT] ||
-	         right != chip->image[OPTi93X_MIXOUT_RIGHT];
-	snd_opti93x_out_image(chip, OPTi93X_MIXOUT_LEFT, left);
-	snd_opti93x_out_image(chip, OPTi93X_MIXOUT_RIGHT, right);
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return change;
-}
-
-#if 0
-
-#define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
-  .info = snd_opti93x_info_single, \
-  .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \
-  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
-
-static int snd_opti93x_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
-static int snd_opti93x_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0xff;
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-	int invert = (kcontrol->private_value >> 24) & 0xff;
-	
-	spin_lock_irqsave(&chip->lock, flags);
-	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
-	spin_unlock_irqrestore(&chip->lock, flags);
-	if (invert)
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-	return 0;
-}
-
-static int snd_opti93x_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int reg = kcontrol->private_value & 0xff;
-	int shift = (kcontrol->private_value >> 8) & 0xff;
-	int mask = (kcontrol->private_value >> 16) & 0xff;
-	int invert = (kcontrol->private_value >> 24) & 0xff;
-	int change;
-	unsigned short val;
-	
-	val = (ucontrol->value.integer.value[0] & mask);
-	if (invert)
-		val = mask - val;
-	val <<= shift;
-	spin_lock_irqsave(&chip->lock, flags);
-	val = (chip->image[reg] & ~(mask << shift)) | val;
-	change = val != chip->image[reg];
-	snd_opti93x_out(chip, reg, val);
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return change;
-}
-
-#endif /* single */
-
-#define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
-  .info = snd_opti93x_info_double, \
-  .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \
-  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
-
-#define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \
-	do { xctl.private_value ^= 22; } while (0)
-#define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \
-	do { xctl.private_value &= ~0x0000ffff; \
-	     xctl.private_value |= left_reg | (right_reg << 8); } while (0)
-
-static int snd_opti93x_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-
-	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = mask;
-	return 0;
-}
-
-static int snd_opti93x_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int left_reg = kcontrol->private_value & 0xff;
-	int right_reg = (kcontrol->private_value >> 8) & 0xff;
-	int shift_left = (kcontrol->private_value >> 16) & 0x07;
-	int shift_right = (kcontrol->private_value >> 19) & 0x07;
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-	int invert = (kcontrol->private_value >> 22) & 1;
-	
-	spin_lock_irqsave(&chip->lock, flags);
-	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
-	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
-	spin_unlock_irqrestore(&chip->lock, flags);
-	if (invert) {
-		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
-	}
-	return 0;
-}
-
-static int snd_opti93x_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int left_reg = kcontrol->private_value & 0xff;
-	int right_reg = (kcontrol->private_value >> 8) & 0xff;
-	int shift_left = (kcontrol->private_value >> 16) & 0x07;
-	int shift_right = (kcontrol->private_value >> 19) & 0x07;
-	int mask = (kcontrol->private_value >> 24) & 0xff;
-	int invert = (kcontrol->private_value >> 22) & 1;
-	int change;
-	unsigned short val1, val2;
-	
-	val1 = ucontrol->value.integer.value[0] & mask;
-	val2 = ucontrol->value.integer.value[1] & mask;
-	if (invert) {
-		val1 = mask - val1;
-		val2 = mask - val2;
-	}
-	val1 <<= shift_left;
-	val2 <<= shift_right;
-	spin_lock_irqsave(&chip->lock, flags);
-	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
-	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
-	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
-	snd_opti93x_out_image(chip, left_reg, val1);
-	snd_opti93x_out_image(chip, right_reg, val2);
-	spin_unlock_irqrestore(&chip->lock, flags);
-	return change;
-}
-
-static struct snd_kcontrol_new snd_opti93x_controls[] __devinitdata = {
-OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), 
-OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 1),
-OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 1, 1, 15, 1), 
-OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), 
-OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1),
-OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 1, 1, 15, 1),
-OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
-OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), 
-OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0),
-{
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Source",
-	.info = snd_opti93x_info_mux,
-	.get = snd_opti93x_get_mux,
-	.put = snd_opti93x_put_mux,
-}
-};
-                                        
-static int __devinit snd_opti93x_mixer(struct snd_opti93x *chip)
-{
-	struct snd_card *card;
-	struct snd_kcontrol_new knew;
-	int err;
-	unsigned int idx;
-
-	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
-
-	card = chip->card;
-
-	strcpy(card->mixername, snd_opti93x_chip_id(chip));
-
-	for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
-		knew = snd_opti93x_controls[idx];
-		if (chip->hardware == OPTi9XX_HW_82C930) {
-			if (strstr(knew.name, "FM"))	/* skip FM controls */
-				continue;
-			else if (strcmp(knew.name, "Mic Playback Volume"))
-				OPTi93X_DOUBLE_INVERT_INVERT(knew);
-			else if (strstr(knew.name, "Aux"))
-				OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT);
-			else if (strcmp(knew.name, "PCM Playback Volume"))
-				OPTi93X_DOUBLE_INVERT_INVERT(knew);
-			else if (strcmp(knew.name, "Master Playback Volume"))
-				OPTi93X_DOUBLE_INVERT_INVERT(knew);
-		}
-		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0)
-			return err;
-	}
-	return 0;
-}
-
 #endif /* OPTi93X */
 
 static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
@@ -1739,8 +685,16 @@
 {
 	struct snd_opti9xx *chip = card->private_data;
         
-	if (chip)
+	if (chip) {
+#ifdef OPTi93X
+		struct snd_cs4231 *codec = chip->codec;
+		if (codec->irq > 0) {
+			disable_irq(codec->irq);
+			free_irq(codec->irq, codec);
+		}
+#endif
 		release_and_free_resource(chip->res_mc_base);
+	}
 }
 
 static int __devinit snd_opti9xx_probe(struct snd_card *card)
@@ -1748,11 +702,11 @@
 	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
 	int error;
 	struct snd_opti9xx *chip = card->private_data;
-#if defined(OPTi93X)
-	struct snd_opti93x *codec;
-#elif defined(CS4231)
+#if defined(CS4231) || defined(OPTi93X)
 	struct snd_cs4231 *codec;
+#ifdef CS4231
 	struct snd_timer *timer;
+#endif
 #else
 	struct snd_ad1848 *codec;
 #endif
@@ -1784,26 +738,34 @@
 	if ((error = snd_opti9xx_configure(chip)))
 		return error;
 
-#if defined(OPTi93X)
-	if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec)))
-		return error;
-	if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0)
-		return error;
-	if ((error = snd_opti93x_mixer(codec)) < 0)
-		return error;
-#elif defined(CS4231)
+#if defined(CS4231) || defined(OPTi93X)
 	if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
 				       chip->irq, chip->dma1, chip->dma2,
-				       CS4231_HW_DETECT,
-				       0,
+#ifdef CS4231
+				       CS4231_HW_DETECT, 0,
+#else /* OPTi93x */
+				       CS4231_HW_OPTI93X, CS4231_HWSHARE_IRQ,
+#endif
 				       &codec)) < 0)
 		return error;
+#ifdef OPTi93X
+	chip->codec = codec;
+#endif
 	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0)
 		return error;
 	if ((error = snd_cs4231_mixer(codec)) < 0)
 		return error;
+#ifdef CS4231
 	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0)
 		return error;
+#else /* OPTI93X */
+	error = request_irq(chip->irq, snd_opti93x_interrupt,
+			    IRQF_DISABLED, DEV_NAME" - WSS", codec);
+	if (error < 0) {
+		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
+		return error;
+	}
+#endif
 #else
 	if ((error = snd_ad1848_create(card, chip->wss_base + 4,
 				       chip->irq, chip->dma1,
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index c9d1c98..1098a56 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -34,5 +34,3 @@
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
 endif
 obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o
-
-obj-m := $(sort $(obj-m))
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 95eeca1..0bb9b92 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1939,7 +1939,7 @@
 wavefront_download_firmware (snd_wavefront_t *dev, char *path)
 
 {
-	unsigned char *buf;
+	const unsigned char *buf;
 	int len, err;
 	int section_cnt_downloaded = 0;
 	const struct firmware *firmware;
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index 531f8ba..a9823fa 100644
--- a/sound/mips/Kconfig
+++ b/sound/mips/Kconfig
@@ -1,15 +1,34 @@
 # ALSA MIPS drivers
 
-menu "ALSA MIPS devices"
-	depends on SND!=n && MIPS
+menuconfig SND_MIPS
+	bool "MIPS sound devices"
+	depends on MIPS
+	default y
+	help
+	  Support for sound devices of MIPS architectures.
+
+if SND_MIPS
+
+config SND_SGI_O2
+	tristate "SGI O2 Audio"
+	depends on SGI_IP32
+        help
+                Sound support for the SGI O2 Workstation. 
+
+config SND_SGI_HAL2
+        tristate "SGI HAL2 Audio"
+        depends on SGI_HAS_HAL2
+        help
+                Sound support for the SGI Indy and Indigo2 Workstation.
+
 
 config SND_AU1X00
 	tristate "Au1x00 AC97 Port Driver"
-	depends on (SOC_AU1000 || SOC_AU1100 || SOC_AU1500) && SND
+	depends on SOC_AU1000 || SOC_AU1100 || SOC_AU1500
 	select SND_PCM
 	select SND_AC97_CODEC
 	help
 	  ALSA Sound driver for the Au1x00's AC97 port.
 
-endmenu
+endif	# SND_MIPS
 
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 47afed9..861ec0a 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -3,6 +3,10 @@
 #
 
 snd-au1x00-objs := au1x00.o
+snd-sgi-o2-objs := sgio2audio.o ad1843.o
+snd-sgi-hal2-objs := hal2.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
+obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
+obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c
new file mode 100644
index 0000000..c624510
--- /dev/null
+++ b/sound/mips/ad1843.c
@@ -0,0 +1,561 @@
+/*
+ *   AD1843 low level driver
+ *
+ *   Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ *   Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ *
+ *   inspired from vwsnd.c (SGI VW audio driver)
+ *     Copyright 1999 Silicon Graphics, Inc.  All rights reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ad1843.h>
+
+/*
+ * AD1843 bitfield definitions.  All are named as in the AD1843 data
+ * sheet, with ad1843_ prepended and individual bit numbers removed.
+ *
+ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
+ *
+ * Only the bitfields we need are defined.
+ */
+
+struct ad1843_bitfield {
+	char reg;
+	char lo_bit;
+	char nbits;
+};
+
+static const struct ad1843_bitfield
+	ad1843_PDNO   = {  0, 14,  1 },	/* Converter Power-Down Flag */
+	ad1843_INIT   = {  0, 15,  1 },	/* Clock Initialization Flag */
+	ad1843_RIG    = {  2,  0,  4 },	/* Right ADC Input Gain */
+	ad1843_RMGE   = {  2,  4,  1 },	/* Right ADC Mic Gain Enable */
+	ad1843_RSS    = {  2,  5,  3 },	/* Right ADC Source Select */
+	ad1843_LIG    = {  2,  8,  4 },	/* Left ADC Input Gain */
+	ad1843_LMGE   = {  2, 12,  1 },	/* Left ADC Mic Gain Enable */
+	ad1843_LSS    = {  2, 13,  3 },	/* Left ADC Source Select */
+	ad1843_RD2M   = {  3,  0,  5 },	/* Right DAC 2 Mix Gain/Atten */
+	ad1843_RD2MM  = {  3,  7,  1 },	/* Right DAC 2 Mix Mute */
+	ad1843_LD2M   = {  3,  8,  5 },	/* Left DAC 2 Mix Gain/Atten */
+	ad1843_LD2MM  = {  3, 15,  1 },	/* Left DAC 2 Mix Mute */
+	ad1843_RX1M   = {  4,  0,  5 },	/* Right Aux 1 Mix Gain/Atten */
+	ad1843_RX1MM  = {  4,  7,  1 },	/* Right Aux 1 Mix Mute */
+	ad1843_LX1M   = {  4,  8,  5 },	/* Left Aux 1 Mix Gain/Atten */
+	ad1843_LX1MM  = {  4, 15,  1 },	/* Left Aux 1 Mix Mute */
+	ad1843_RX2M   = {  5,  0,  5 },	/* Right Aux 2 Mix Gain/Atten */
+	ad1843_RX2MM  = {  5,  7,  1 },	/* Right Aux 2 Mix Mute */
+	ad1843_LX2M   = {  5,  8,  5 },	/* Left Aux 2 Mix Gain/Atten */
+	ad1843_LX2MM  = {  5, 15,  1 },	/* Left Aux 2 Mix Mute */
+	ad1843_RMCM   = {  7,  0,  5 },	/* Right Mic Mix Gain/Atten */
+	ad1843_RMCMM  = {  7,  7,  1 },	/* Right Mic Mix Mute */
+	ad1843_LMCM   = {  7,  8,  5 },	/* Left Mic Mix Gain/Atten */
+	ad1843_LMCMM  = {  7, 15,  1 },	/* Left Mic Mix Mute */
+	ad1843_HPOS   = {  8,  4,  1 },	/* Headphone Output Voltage Swing */
+	ad1843_HPOM   = {  8,  5,  1 },	/* Headphone Output Mute */
+	ad1843_MPOM   = {  8,  6,  1 },	/* Mono Output Mute */
+	ad1843_RDA1G  = {  9,  0,  6 },	/* Right DAC1 Analog/Digital Gain */
+	ad1843_RDA1GM = {  9,  7,  1 },	/* Right DAC1 Analog Mute */
+	ad1843_LDA1G  = {  9,  8,  6 },	/* Left DAC1 Analog/Digital Gain */
+	ad1843_LDA1GM = {  9, 15,  1 },	/* Left DAC1 Analog Mute */
+	ad1843_RDA2G  = { 10,  0,  6 },	/* Right DAC2 Analog/Digital Gain */
+	ad1843_RDA2GM = { 10,  7,  1 },	/* Right DAC2 Analog Mute */
+	ad1843_LDA2G  = { 10,  8,  6 },	/* Left DAC2 Analog/Digital Gain */
+	ad1843_LDA2GM = { 10, 15,  1 },	/* Left DAC2 Analog Mute */
+	ad1843_RDA1AM = { 11,  7,  1 },	/* Right DAC1 Digital Mute */
+	ad1843_LDA1AM = { 11, 15,  1 },	/* Left DAC1 Digital Mute */
+	ad1843_RDA2AM = { 12,  7,  1 },	/* Right DAC2 Digital Mute */
+	ad1843_LDA2AM = { 12, 15,  1 },	/* Left DAC2 Digital Mute */
+	ad1843_ADLC   = { 15,  0,  2 },	/* ADC Left Sample Rate Source */
+	ad1843_ADRC   = { 15,  2,  2 },	/* ADC Right Sample Rate Source */
+	ad1843_DA1C   = { 15,  8,  2 },	/* DAC1 Sample Rate Source */
+	ad1843_DA2C   = { 15, 10,  2 },	/* DAC2 Sample Rate Source */
+	ad1843_C1C    = { 17,  0, 16 },	/* Clock 1 Sample Rate Select */
+	ad1843_C2C    = { 20,  0, 16 },	/* Clock 2 Sample Rate Select */
+	ad1843_C3C    = { 23,  0, 16 },	/* Clock 3 Sample Rate Select */
+	ad1843_DAADL  = { 25,  4,  2 },	/* Digital ADC Left Source Select */
+	ad1843_DAADR  = { 25,  6,  2 },	/* Digital ADC Right Source Select */
+	ad1843_DAMIX  = { 25, 14,  1 },	/* DAC Digital Mix Enable */
+	ad1843_DRSFLT = { 25, 15,  1 },	/* Digital Reampler Filter Mode */
+	ad1843_ADLF   = { 26,  0,  2 }, /* ADC Left Channel Data Format */
+	ad1843_ADRF   = { 26,  2,  2 }, /* ADC Right Channel Data Format */
+	ad1843_ADTLK  = { 26,  4,  1 },	/* ADC Transmit Lock Mode Select */
+	ad1843_SCF    = { 26,  7,  1 },	/* SCLK Frequency Select */
+	ad1843_DA1F   = { 26,  8,  2 },	/* DAC1 Data Format Select */
+	ad1843_DA2F   = { 26, 10,  2 },	/* DAC2 Data Format Select */
+	ad1843_DA1SM  = { 26, 14,  1 },	/* DAC1 Stereo/Mono Mode Select */
+	ad1843_DA2SM  = { 26, 15,  1 },	/* DAC2 Stereo/Mono Mode Select */
+	ad1843_ADLEN  = { 27,  0,  1 },	/* ADC Left Channel Enable */
+	ad1843_ADREN  = { 27,  1,  1 },	/* ADC Right Channel Enable */
+	ad1843_AAMEN  = { 27,  4,  1 },	/* Analog to Analog Mix Enable */
+	ad1843_ANAEN  = { 27,  7,  1 },	/* Analog Channel Enable */
+	ad1843_DA1EN  = { 27,  8,  1 },	/* DAC1 Enable */
+	ad1843_DA2EN  = { 27,  9,  1 },	/* DAC2 Enable */
+	ad1843_DDMEN  = { 27, 12,  1 },	/* DAC2 to DAC1 Mix  Enable */
+	ad1843_C1EN   = { 28, 11,  1 },	/* Clock Generator 1 Enable */
+	ad1843_C2EN   = { 28, 12,  1 },	/* Clock Generator 2 Enable */
+	ad1843_C3EN   = { 28, 13,  1 },	/* Clock Generator 3 Enable */
+	ad1843_PDNI   = { 28, 15,  1 };	/* Converter Power Down */
+
+/*
+ * The various registers of the AD1843 use three different formats for
+ * specifying gain.  The ad1843_gain structure parameterizes the
+ * formats.
+ */
+
+struct ad1843_gain {
+	int	negative;		/* nonzero if gain is negative. */
+	const struct ad1843_bitfield *lfield;
+	const struct ad1843_bitfield *rfield;
+	const struct ad1843_bitfield *lmute;
+	const struct ad1843_bitfield *rmute;
+};
+
+static const struct ad1843_gain ad1843_gain_RECLEV = {
+	.negative = 0,
+	.lfield   = &ad1843_LIG,
+	.rfield   = &ad1843_RIG
+};
+static const struct ad1843_gain ad1843_gain_LINE = {
+	.negative = 1,
+	.lfield   = &ad1843_LX1M,
+	.rfield   = &ad1843_RX1M,
+	.lmute    = &ad1843_LX1MM,
+	.rmute    = &ad1843_RX1MM
+};
+static const struct ad1843_gain ad1843_gain_LINE_2 = {
+	.negative = 1,
+	.lfield   = &ad1843_LDA2G,
+	.rfield   = &ad1843_RDA2G,
+	.lmute    = &ad1843_LDA2GM,
+	.rmute    = &ad1843_RDA2GM
+};
+static const struct ad1843_gain ad1843_gain_MIC = {
+	.negative = 1,
+	.lfield   = &ad1843_LMCM,
+	.rfield   = &ad1843_RMCM,
+	.lmute    = &ad1843_LMCMM,
+	.rmute    = &ad1843_RMCMM
+};
+static const struct ad1843_gain ad1843_gain_PCM_0 = {
+	.negative = 1,
+	.lfield   = &ad1843_LDA1G,
+	.rfield   = &ad1843_RDA1G,
+	.lmute    = &ad1843_LDA1GM,
+	.rmute    = &ad1843_RDA1GM
+};
+static const struct ad1843_gain ad1843_gain_PCM_1 = {
+	.negative = 1,
+	.lfield   = &ad1843_LD2M,
+	.rfield   = &ad1843_RD2M,
+	.lmute    = &ad1843_LD2MM,
+	.rmute    = &ad1843_RD2MM
+};
+
+static const struct ad1843_gain *ad1843_gain[AD1843_GAIN_SIZE] =
+{
+	&ad1843_gain_RECLEV,
+	&ad1843_gain_LINE,
+	&ad1843_gain_LINE_2,
+	&ad1843_gain_MIC,
+	&ad1843_gain_PCM_0,
+	&ad1843_gain_PCM_1,
+};
+
+/* read the current value of an AD1843 bitfield. */
+
+static int ad1843_read_bits(struct snd_ad1843 *ad1843,
+			    const struct ad1843_bitfield *field)
+{
+	int w;
+
+	w = ad1843->read(ad1843->chip, field->reg);
+	return w >> field->lo_bit & ((1 << field->nbits) - 1);
+}
+
+/*
+ * write a new value to an AD1843 bitfield and return the old value.
+ */
+
+static int ad1843_write_bits(struct snd_ad1843 *ad1843,
+			     const struct ad1843_bitfield *field,
+			     int newval)
+{
+	int w, mask, oldval, newbits;
+
+	w = ad1843->read(ad1843->chip, field->reg);
+	mask = ((1 << field->nbits) - 1) << field->lo_bit;
+	oldval = (w & mask) >> field->lo_bit;
+	newbits = (newval << field->lo_bit) & mask;
+	w = (w & ~mask) | newbits;
+	ad1843->write(ad1843->chip, field->reg, w);
+
+	return oldval;
+}
+
+/*
+ * ad1843_read_multi reads multiple bitfields from the same AD1843
+ * register.  It uses a single read cycle to do it.  (Reading the
+ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
+ * microseconds.)
+ *
+ * Called like this.
+ *
+ *  ad1843_read_multi(ad1843, nfields,
+ *		      &ad1843_FIELD1, &val1,
+ *		      &ad1843_FIELD2, &val2, ...);
+ */
+
+static void ad1843_read_multi(struct snd_ad1843 *ad1843, int argcount, ...)
+{
+	va_list ap;
+	const struct ad1843_bitfield *fp;
+	int w = 0, mask, *value, reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const struct ad1843_bitfield *);
+		value = va_arg(ap, int *);
+		if (reg == -1) {
+			reg = fp->reg;
+			w = ad1843->read(ad1843->chip, reg);
+		}
+
+		mask = (1 << fp->nbits) - 1;
+		*value = w >> fp->lo_bit & mask;
+	}
+	va_end(ap);
+}
+
+/*
+ * ad1843_write_multi stores multiple bitfields into the same AD1843
+ * register.  It uses one read and one write cycle to do it.
+ *
+ * Called like this.
+ *
+ *  ad1843_write_multi(ad1843, nfields,
+ *		       &ad1843_FIELD1, val1,
+ *		       &ad1843_FIELF2, val2, ...);
+ */
+
+static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
+{
+	va_list ap;
+	int reg;
+	const struct ad1843_bitfield *fp;
+	int value;
+	int w, m, mask, bits;
+
+	mask = 0;
+	bits = 0;
+	reg = -1;
+
+	va_start(ap, argcount);
+	while (--argcount >= 0) {
+		fp = va_arg(ap, const struct ad1843_bitfield *);
+		value = va_arg(ap, int);
+		if (reg == -1)
+			reg = fp->reg;
+		else
+			BUG_ON(reg != fp->reg);
+		m = ((1 << fp->nbits) - 1) << fp->lo_bit;
+		mask |= m;
+		bits |= (value << fp->lo_bit) & m;
+	}
+	va_end(ap);
+
+	if (~mask & 0xFFFF)
+		w = ad1843->read(ad1843->chip, reg);
+	else
+		w = 0;
+	w = (w & ~mask) | bits;
+	ad1843->write(ad1843->chip, reg, w);
+}
+
+int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id)
+{
+	const struct ad1843_gain *gp = ad1843_gain[id];
+	int ret;
+
+	ret = (1 << gp->lfield->nbits);
+	if (!gp->lmute)
+		ret -= 1;
+	return ret;
+}
+
+/*
+ * ad1843_get_gain reads the specified register and extracts the gain value
+ * using the supplied gain type.
+ */
+
+int ad1843_get_gain(struct snd_ad1843 *ad1843, int id)
+{
+	int lg, rg, lm, rm;
+	const struct ad1843_gain *gp = ad1843_gain[id];
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	ad1843_read_multi(ad1843, 2, gp->lfield, &lg, gp->rfield, &rg);
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	if (gp->lmute) {
+		ad1843_read_multi(ad1843, 2, gp->lmute, &lm, gp->rmute, &rm);
+		if (lm)
+			lg = 0;
+		if (rm)
+			rg = 0;
+	}
+	return lg << 0 | rg << 8;
+}
+
+/*
+ * Set an audio channel's gain.
+ *
+ * Returns the new gain, which may be lower than the old gain.
+ */
+
+int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval)
+{
+	const struct ad1843_gain *gp = ad1843_gain[id];
+	unsigned short mask = (1 << gp->lfield->nbits) - 1;
+
+	int lg = (newval >> 0) & mask;
+	int rg = (newval >> 8) & mask;
+	int lm = (lg == 0) ? 1 : 0;
+	int rm = (rg == 0) ? 1 : 0;
+
+	if (gp->negative) {
+		lg = mask - lg;
+		rg = mask - rg;
+	}
+	if (gp->lmute)
+		ad1843_write_multi(ad1843, 2, gp->lmute, lm, gp->rmute, rm);
+	ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg);
+	return ad1843_get_gain(ad1843, id);
+}
+
+/* Returns the current recording source */
+
+int ad1843_get_recsrc(struct snd_ad1843 *ad1843)
+{
+	int val = ad1843_read_bits(ad1843, &ad1843_LSS);
+
+	if (val < 0 || val > 2) {
+		val = 2;
+		ad1843_write_multi(ad1843, 2,
+				   &ad1843_LSS, val, &ad1843_RSS, val);
+	}
+	return val;
+}
+
+/*
+ * Set recording source.
+ *
+ * Returns newsrc on success, -errno on failure.
+ */
+
+int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc)
+{
+	if (newsrc < 0 || newsrc > 2)
+		return -EINVAL;
+
+	ad1843_write_multi(ad1843, 2, &ad1843_LSS, newsrc, &ad1843_RSS, newsrc);
+	return newsrc;
+}
+
+/* Setup ad1843 for D/A conversion. */
+
+void ad1843_setup_dac(struct snd_ad1843 *ad1843,
+		      unsigned int id,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels)
+{
+	int ad_fmt = 0, ad_mode = 0;
+
+	switch (fmt) {
+	case SNDRV_PCM_FORMAT_S8:
+		ad_fmt = 0;
+		break;
+	case SNDRV_PCM_FORMAT_U8:
+		ad_fmt = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ad_fmt = 1;
+		break;
+	case SNDRV_PCM_FORMAT_MU_LAW:
+		ad_fmt = 2;
+		break;
+	case SNDRV_PCM_FORMAT_A_LAW:
+		ad_fmt = 3;
+		break;
+	default:
+		break;
+	}
+
+	switch (channels) {
+	case 2:
+		ad_mode = 0;
+		break;
+	case 1:
+		ad_mode = 1;
+		break;
+	default:
+		break;
+	}
+
+	if (id) {
+		ad1843_write_bits(ad1843, &ad1843_C2C, framerate);
+		ad1843_write_multi(ad1843, 2,
+				   &ad1843_DA2SM, ad_mode,
+				   &ad1843_DA2F, ad_fmt);
+	} else {
+		ad1843_write_bits(ad1843, &ad1843_C1C, framerate);
+		ad1843_write_multi(ad1843, 2,
+				   &ad1843_DA1SM, ad_mode,
+				   &ad1843_DA1F, ad_fmt);
+	}
+}
+
+void ad1843_shutdown_dac(struct snd_ad1843 *ad1843, unsigned int id)
+{
+	if (id)
+		ad1843_write_bits(ad1843, &ad1843_DA2F, 1);
+	else
+		ad1843_write_bits(ad1843, &ad1843_DA1F, 1);
+}
+
+void ad1843_setup_adc(struct snd_ad1843 *ad1843,
+		      unsigned int framerate,
+		      snd_pcm_format_t fmt,
+		      unsigned int channels)
+{
+	int da_fmt = 0;
+
+	switch (fmt) {
+	case SNDRV_PCM_FORMAT_S8:	da_fmt = 0; break;
+	case SNDRV_PCM_FORMAT_U8:	da_fmt = 0; break;
+	case SNDRV_PCM_FORMAT_S16_LE:	da_fmt = 1; break;
+	case SNDRV_PCM_FORMAT_MU_LAW:	da_fmt = 2; break;
+	case SNDRV_PCM_FORMAT_A_LAW:	da_fmt = 3; break;
+	default:		break;
+	}
+
+	ad1843_write_bits(ad1843, &ad1843_C3C, framerate);
+	ad1843_write_multi(ad1843, 2,
+			   &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
+}
+
+void ad1843_shutdown_adc(struct snd_ad1843 *ad1843)
+{
+	/* nothing to do */
+}
+
+/*
+ * Fully initialize the ad1843.  As described in the AD1843 data
+ * sheet, section "START-UP SEQUENCE".  The numbered comments are
+ * subsection headings from the data sheet.  See the data sheet, pages
+ * 52-54, for more info.
+ *
+ * return 0 on success, -errno on failure.  */
+
+int ad1843_init(struct snd_ad1843 *ad1843)
+{
+	unsigned long later;
+
+	if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) {
+		printk(KERN_ERR "ad1843: AD1843 won't initialize\n");
+		return -EIO;
+	}
+
+	ad1843_write_bits(ad1843, &ad1843_SCF, 1);
+
+	/* 4. Put the conversion resources into standby. */
+	ad1843_write_bits(ad1843, &ad1843_PDNI, 0);
+	later = jiffies + msecs_to_jiffies(500);
+
+	while (ad1843_read_bits(ad1843, &ad1843_PDNO)) {
+		if (time_after(jiffies, later)) {
+			printk(KERN_ERR
+			       "ad1843: AD1843 won't power up\n");
+			return -EIO;
+		}
+		schedule_timeout_interruptible(5);
+	}
+
+	/* 5. Power up the clock generators and enable clock output pins. */
+	ad1843_write_multi(ad1843, 3,
+			   &ad1843_C1EN, 1,
+			   &ad1843_C2EN, 1,
+			   &ad1843_C3EN, 1);
+
+	/* 6. Configure conversion resources while they are in standby. */
+
+	/* DAC1/2 use clock 1/2 as source, ADC uses clock 3.  Always. */
+	ad1843_write_multi(ad1843, 4,
+			   &ad1843_DA1C, 1,
+			   &ad1843_DA2C, 2,
+			   &ad1843_ADLC, 3,
+			   &ad1843_ADRC, 3);
+
+	/* 7. Enable conversion resources. */
+	ad1843_write_bits(ad1843, &ad1843_ADTLK, 1);
+	ad1843_write_multi(ad1843, 7,
+			   &ad1843_ANAEN, 1,
+			   &ad1843_AAMEN, 1,
+			   &ad1843_DA1EN, 1,
+			   &ad1843_DA2EN, 1,
+			   &ad1843_DDMEN, 1,
+			   &ad1843_ADLEN, 1,
+			   &ad1843_ADREN, 1);
+
+	/* 8. Configure conversion resources while they are enabled. */
+
+	/* set gain to 0 for all channels */
+	ad1843_set_gain(ad1843, AD1843_GAIN_RECLEV, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_LINE, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_LINE_2, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_MIC, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_PCM_0, 0);
+	ad1843_set_gain(ad1843, AD1843_GAIN_PCM_1, 0);
+
+	/* Unmute all channels. */
+	/* DAC1 */
+	ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0);
+	/* DAC2 */
+	ad1843_write_multi(ad1843, 2, &ad1843_LDA2GM, 0, &ad1843_RDA2GM, 0);
+
+	/* Set default recording source to Line In and set
+	 * mic gain to +20 dB.
+	 */
+	ad1843_set_recsrc(ad1843, 2);
+	ad1843_write_multi(ad1843, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
+
+	/* Set Speaker Out level to +/- 4V and unmute it. */
+	ad1843_write_multi(ad1843, 3,
+			   &ad1843_HPOS, 1,
+			   &ad1843_HPOM, 0,
+			   &ad1843_MPOM, 0);
+
+	return 0;
+}
diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c
new file mode 100644
index 0000000..db495be
--- /dev/null
+++ b/sound/mips/hal2.c
@@ -0,0 +1,947 @@
+/*
+ *  Driver for A2 audio system used in SGI machines
+ *  Copyright (c) 2008 Thomas Bogendoerfer <tsbogend@alpha.fanken.de>
+ *
+ *  Based on OSS code from Ladislav Michl <ladis@linux-mips.org>, which
+ *  was based on code from Ulf Carlsson
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm-indirect.h>
+#include <sound/initval.h>
+
+#include "hal2.h"
+
+static int index = SNDRV_DEFAULT_IDX1;  /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;   /* ID for this card */
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SGI HAL2 soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SGI HAL2 soundcard.");
+MODULE_DESCRIPTION("ALSA driver for SGI HAL2 audio");
+MODULE_AUTHOR("Thomas Bogendoerfer");
+MODULE_LICENSE("GPL");
+
+
+#define H2_BLOCK_SIZE	1024
+#define H2_BUF_SIZE	16384
+
+struct hal2_pbus {
+	struct hpc3_pbus_dmacregs *pbus;
+	int pbusnr;
+	unsigned int ctrl;		/* Current state of pbus->pbdma_ctrl */
+};
+
+struct hal2_desc {
+	struct hpc_dma_desc desc;
+	u32 pad;			/* padding */
+};
+
+struct hal2_codec {
+	struct snd_pcm_indirect pcm_indirect;
+	struct snd_pcm_substream *substream;
+
+	unsigned char *buffer;
+	dma_addr_t buffer_dma;
+	struct hal2_desc *desc;
+	dma_addr_t desc_dma;
+	int desc_count;
+	struct hal2_pbus pbus;
+	int voices;			/* mono/stereo */
+	unsigned int sample_rate;
+	unsigned int master;		/* Master frequency */
+	unsigned short mod;		/* MOD value */
+	unsigned short inc;		/* INC value */
+};
+
+#define H2_MIX_OUTPUT_ATT	0
+#define H2_MIX_INPUT_GAIN	1
+
+struct snd_hal2 {
+	struct snd_card *card;
+
+	struct hal2_ctl_regs *ctl_regs;	/* HAL2 ctl registers */
+	struct hal2_aes_regs *aes_regs;	/* HAL2 aes registers */
+	struct hal2_vol_regs *vol_regs;	/* HAL2 vol registers */
+	struct hal2_syn_regs *syn_regs;	/* HAL2 syn registers */
+
+	struct hal2_codec dac;
+	struct hal2_codec adc;
+};
+
+#define H2_INDIRECT_WAIT(regs)	while (hal2_read(&regs->isr) & H2_ISR_TSTATUS);
+
+#define H2_READ_ADDR(addr)	(addr | (1<<7))
+#define H2_WRITE_ADDR(addr)	(addr)
+
+static inline u32 hal2_read(u32 *reg)
+{
+	return __raw_readl(reg);
+}
+
+static inline void hal2_write(u32 val, u32 *reg)
+{
+	__raw_writel(val, reg);
+}
+
+
+static u32 hal2_i_read32(struct snd_hal2 *hal2, u16 addr)
+{
+	u32 ret;
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(H2_READ_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	ret = hal2_read(&regs->idr0) & 0xffff;
+	hal2_write(H2_READ_ADDR(addr) | 0x1, &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	ret |= (hal2_read(&regs->idr0) & 0xffff) << 16;
+	return ret;
+}
+
+static void hal2_i_write16(struct snd_hal2 *hal2, u16 addr, u16 val)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(val, &regs->idr0);
+	hal2_write(0, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_write32(struct snd_hal2 *hal2, u16 addr, u32 val)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(val & 0xffff, &regs->idr0);
+	hal2_write(val >> 16, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_setbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(H2_READ_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	hal2_write((hal2_read(&regs->idr0) & 0xffff) | bit, &regs->idr0);
+	hal2_write(0, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static void hal2_i_clearbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
+{
+	struct hal2_ctl_regs *regs = hal2->ctl_regs;
+
+	hal2_write(H2_READ_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+	hal2_write((hal2_read(&regs->idr0) & 0xffff) & ~bit, &regs->idr0);
+	hal2_write(0, &regs->idr1);
+	hal2_write(0, &regs->idr2);
+	hal2_write(0, &regs->idr3);
+	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
+	H2_INDIRECT_WAIT(regs);
+}
+
+static int hal2_gain_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	switch ((int)kcontrol->private_value) {
+	case H2_MIX_OUTPUT_ATT:
+		uinfo->value.integer.max = 31;
+		break;
+	case H2_MIX_INPUT_GAIN:
+		uinfo->value.integer.max = 15;
+		break;
+	}
+	return 0;
+}
+
+static int hal2_gain_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
+	u32 tmp;
+	int l, r;
+
+	switch ((int)kcontrol->private_value) {
+	case H2_MIX_OUTPUT_ATT:
+		tmp = hal2_i_read32(hal2, H2I_DAC_C2);
+		if (tmp & H2I_C2_MUTE) {
+			l = 0;
+			r = 0;
+		} else {
+			l = 31 - ((tmp >> H2I_C2_L_ATT_SHIFT) & 31);
+			r = 31 - ((tmp >> H2I_C2_R_ATT_SHIFT) & 31);
+		}
+		break;
+	case H2_MIX_INPUT_GAIN:
+		tmp = hal2_i_read32(hal2, H2I_ADC_C2);
+		l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15;
+		r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15;
+		break;
+	}
+	ucontrol->value.integer.value[0] = l;
+	ucontrol->value.integer.value[1] = r;
+
+	return 0;
+}
+
+static int hal2_gain_put(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
+	u32 old, new;
+	int l, r;
+
+	l = ucontrol->value.integer.value[0];
+	r = ucontrol->value.integer.value[1];
+
+	switch ((int)kcontrol->private_value) {
+	case H2_MIX_OUTPUT_ATT:
+		old = hal2_i_read32(hal2, H2I_DAC_C2);
+		new = old & ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
+		if (l | r) {
+			l = 31 - l;
+			r = 31 - r;
+			new |= (l << H2I_C2_L_ATT_SHIFT);
+			new |= (r << H2I_C2_R_ATT_SHIFT);
+		} else
+			new |= H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE;
+		hal2_i_write32(hal2, H2I_DAC_C2, new);
+		break;
+	case H2_MIX_INPUT_GAIN:
+		old = hal2_i_read32(hal2, H2I_ADC_C2);
+		new = old & ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M);
+		new |= (l << H2I_C2_L_GAIN_SHIFT);
+		new |= (r << H2I_C2_R_GAIN_SHIFT);
+		hal2_i_write32(hal2, H2I_ADC_C2, new);
+		break;
+	}
+	return old != new;
+}
+
+static struct snd_kcontrol_new hal2_ctrl_headphone __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Headphone Playback Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = H2_MIX_OUTPUT_ATT,
+	.info           = hal2_gain_info,
+	.get            = hal2_gain_get,
+	.put            = hal2_gain_put,
+};
+
+static struct snd_kcontrol_new hal2_ctrl_mic __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Mic Capture Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = H2_MIX_INPUT_GAIN,
+	.info           = hal2_gain_info,
+	.get            = hal2_gain_get,
+	.put            = hal2_gain_put,
+};
+
+static int __devinit hal2_mixer_create(struct snd_hal2 *hal2)
+{
+	int err;
+
+	/* mute DAC */
+	hal2_i_write32(hal2, H2I_DAC_C2,
+		       H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
+	/* mute ADC */
+	hal2_i_write32(hal2, H2I_ADC_C2, 0);
+
+	err = snd_ctl_add(hal2->card,
+			  snd_ctl_new1(&hal2_ctrl_headphone, hal2));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(hal2->card,
+			  snd_ctl_new1(&hal2_ctrl_mic, hal2));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static irqreturn_t hal2_interrupt(int irq, void *dev_id)
+{
+	struct snd_hal2 *hal2 = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+
+	/* decide what caused this interrupt */
+	if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
+		snd_pcm_period_elapsed(hal2->dac.substream);
+		ret = IRQ_HANDLED;
+	}
+	if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
+		snd_pcm_period_elapsed(hal2->adc.substream);
+		ret = IRQ_HANDLED;
+	}
+	return ret;
+}
+
+static int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate)
+{
+	unsigned short mod;
+
+	if (44100 % rate < 48000 % rate) {
+		mod = 4 * 44100 / rate;
+		codec->master = 44100;
+	} else {
+		mod = 4 * 48000 / rate;
+		codec->master = 48000;
+	}
+
+	codec->inc = 4;
+	codec->mod = mod;
+	rate = 4 * codec->master / mod;
+
+	return rate;
+}
+
+static void hal2_set_dac_rate(struct snd_hal2 *hal2)
+{
+	unsigned int master = hal2->dac.master;
+	int inc = hal2->dac.inc;
+	int mod = hal2->dac.mod;
+
+	hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0);
+	hal2_i_write32(hal2, H2I_BRES1_C2,
+		       ((0xffff & (inc - mod - 1)) << 16) | inc);
+}
+
+static void hal2_set_adc_rate(struct snd_hal2 *hal2)
+{
+	unsigned int master = hal2->adc.master;
+	int inc = hal2->adc.inc;
+	int mod = hal2->adc.mod;
+
+	hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0);
+	hal2_i_write32(hal2, H2I_BRES2_C2,
+		       ((0xffff & (inc - mod - 1)) << 16) | inc);
+}
+
+static void hal2_setup_dac(struct snd_hal2 *hal2)
+{
+	unsigned int fifobeg, fifoend, highwater, sample_size;
+	struct hal2_pbus *pbus = &hal2->dac.pbus;
+
+	/* Now we set up some PBUS information. The PBUS needs information about
+	 * what portion of the fifo it will use. If it's receiving or
+	 * transmitting, and finally whether the stream is little endian or big
+	 * endian. The information is written later, on the start call.
+	 */
+	sample_size = 2 * hal2->dac.voices;
+	/* Fifo should be set to hold exactly four samples. Highwater mark
+	 * should be set to two samples. */
+	highwater = (sample_size * 2) >> 1;	/* halfwords */
+	fifobeg = 0;				/* playback is first */
+	fifoend = (sample_size * 4) >> 3;	/* doublewords */
+	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD |
+		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24);
+	/* We disable everything before we do anything at all */
+	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
+	/* Setup the HAL2 for playback */
+	hal2_set_dac_rate(hal2);
+	/* Set endianess */
+	hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
+	/* Set DMA bus */
+	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
+	/* We are using 1st Bresenham clock generator for playback */
+	hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
+			| (1 << H2I_C1_CLKID_SHIFT)
+			| (hal2->dac.voices << H2I_C1_DATAT_SHIFT));
+}
+
+static void hal2_setup_adc(struct snd_hal2 *hal2)
+{
+	unsigned int fifobeg, fifoend, highwater, sample_size;
+	struct hal2_pbus *pbus = &hal2->adc.pbus;
+
+	sample_size = 2 * hal2->adc.voices;
+	highwater = (sample_size * 2) >> 1;		/* halfwords */
+	fifobeg = (4 * 4) >> 3;				/* record is second */
+	fifoend = (4 * 4 + sample_size * 4) >> 3;	/* doublewords */
+	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD |
+		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24);
+	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
+	/* Setup the HAL2 for record */
+	hal2_set_adc_rate(hal2);
+	/* Set endianess */
+	hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
+	/* Set DMA bus */
+	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
+	/* We are using 2nd Bresenham clock generator for record */
+	hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
+			| (2 << H2I_C1_CLKID_SHIFT)
+			| (hal2->adc.voices << H2I_C1_DATAT_SHIFT));
+}
+
+static void hal2_start_dac(struct snd_hal2 *hal2)
+{
+	struct hal2_pbus *pbus = &hal2->dac.pbus;
+
+	pbus->pbus->pbdma_dptr = hal2->dac.desc_dma;
+	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
+	/* enable DAC */
+	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
+}
+
+static void hal2_start_adc(struct snd_hal2 *hal2)
+{
+	struct hal2_pbus *pbus = &hal2->adc.pbus;
+
+	pbus->pbus->pbdma_dptr = hal2->adc.desc_dma;
+	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
+	/* enable ADC */
+	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
+}
+
+static inline void hal2_stop_dac(struct snd_hal2 *hal2)
+{
+	hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+	/* The HAL2 itself may remain enabled safely */
+}
+
+static inline void hal2_stop_adc(struct snd_hal2 *hal2)
+{
+	hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
+}
+
+static int hal2_alloc_dmabuf(struct hal2_codec *codec)
+{
+	struct hal2_desc *desc;
+	dma_addr_t desc_dma, buffer_dma;
+	int count = H2_BUF_SIZE / H2_BLOCK_SIZE;
+	int i;
+
+	codec->buffer = dma_alloc_noncoherent(NULL, H2_BUF_SIZE,
+					      &buffer_dma, GFP_KERNEL);
+	if (!codec->buffer)
+		return -ENOMEM;
+	desc = dma_alloc_noncoherent(NULL, count * sizeof(struct hal2_desc),
+				     &desc_dma, GFP_KERNEL);
+	if (!desc) {
+		dma_free_noncoherent(NULL, H2_BUF_SIZE,
+				     codec->buffer, buffer_dma);
+		return -ENOMEM;
+	}
+	codec->buffer_dma = buffer_dma;
+	codec->desc_dma = desc_dma;
+	codec->desc = desc;
+	for (i = 0; i < count; i++) {
+		desc->desc.pbuf = buffer_dma + i * H2_BLOCK_SIZE;
+		desc->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE;
+		desc->desc.pnext = (i == count - 1) ?
+		      desc_dma : desc_dma + (i + 1) * sizeof(struct hal2_desc);
+		desc++;
+	}
+	dma_cache_sync(NULL, codec->desc, count * sizeof(struct hal2_desc),
+		       DMA_TO_DEVICE);
+	codec->desc_count = count;
+	return 0;
+}
+
+static void hal2_free_dmabuf(struct hal2_codec *codec)
+{
+	dma_free_noncoherent(NULL, codec->desc_count * sizeof(struct hal2_desc),
+			     codec->desc, codec->desc_dma);
+	dma_free_noncoherent(NULL, H2_BUF_SIZE, codec->buffer,
+			     codec->buffer_dma);
+}
+
+static struct snd_pcm_hardware hal2_pcm_hw = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
+	.rates =            SNDRV_PCM_RATE_8000_48000,
+	.rate_min =         8000,
+	.rate_max =         48000,
+	.channels_min =     2,
+	.channels_max =     2,
+	.buffer_bytes_max = 65536,
+	.period_bytes_min = 1024,
+	.period_bytes_max = 65536,
+	.periods_min =      2,
+	.periods_max =      1024,
+};
+
+static int hal2_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	int err;
+
+	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int hal2_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int hal2_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	int err;
+
+	runtime->hw = hal2_pcm_hw;
+
+	err = hal2_alloc_dmabuf(&hal2->dac);
+	if (err)
+		return err;
+	return 0;
+}
+
+static int hal2_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	hal2_free_dmabuf(&hal2->dac);
+	return 0;
+}
+
+static int hal2_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hal2_codec *dac = &hal2->dac;
+
+	dac->voices = runtime->channels;
+	dac->sample_rate = hal2_compute_rate(dac, runtime->rate);
+	memset(&dac->pcm_indirect, 0, sizeof(dac->pcm_indirect));
+	dac->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
+	dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	dac->substream = substream;
+	hal2_setup_dac(hal2);
+	return 0;
+}
+
+static int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		hal2->dac.pcm_indirect.hw_io = hal2->dac.buffer_dma;
+		hal2->dac.pcm_indirect.hw_data = 0;
+		substream->ops->ack(substream);
+		hal2_start_dac(hal2);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		hal2_stop_dac(hal2);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t
+hal2_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *dac = &hal2->dac;
+
+	return snd_pcm_indirect_playback_pointer(substream, &dac->pcm_indirect,
+						 dac->pbus.pbus->pbdma_bptr);
+}
+
+static void hal2_playback_transfer(struct snd_pcm_substream *substream,
+				   struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	unsigned char *buf = hal2->dac.buffer + rec->hw_data;
+
+	memcpy(buf, substream->runtime->dma_area + rec->sw_data, bytes);
+	dma_cache_sync(NULL, buf, bytes, DMA_TO_DEVICE);
+
+}
+
+static int hal2_playback_ack(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *dac = &hal2->dac;
+
+	dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+	snd_pcm_indirect_playback_transfer(substream,
+					   &dac->pcm_indirect,
+					   hal2_playback_transfer);
+	return 0;
+}
+
+static int hal2_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *adc = &hal2->adc;
+	int err;
+
+	runtime->hw = hal2_pcm_hw;
+
+	err = hal2_alloc_dmabuf(adc);
+	if (err)
+		return err;
+	return 0;
+}
+
+static int hal2_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	hal2_free_dmabuf(&hal2->adc);
+	return 0;
+}
+
+static int hal2_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hal2_codec *adc = &hal2->adc;
+
+	adc->voices = runtime->channels;
+	adc->sample_rate = hal2_compute_rate(adc, runtime->rate);
+	memset(&adc->pcm_indirect, 0, sizeof(adc->pcm_indirect));
+	adc->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
+	adc->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+	adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	adc->substream = substream;
+	hal2_setup_adc(hal2);
+	return 0;
+}
+
+static int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		hal2->adc.pcm_indirect.hw_io = hal2->adc.buffer_dma;
+		hal2->adc.pcm_indirect.hw_data = 0;
+		printk(KERN_DEBUG "buffer_dma %x\n", hal2->adc.buffer_dma);
+		hal2_start_adc(hal2);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		hal2_stop_adc(hal2);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t
+hal2_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *adc = &hal2->adc;
+
+	return snd_pcm_indirect_capture_pointer(substream, &adc->pcm_indirect,
+						adc->pbus.pbus->pbdma_bptr);
+}
+
+static void hal2_capture_transfer(struct snd_pcm_substream *substream,
+				  struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	unsigned char *buf = hal2->adc.buffer + rec->hw_data;
+
+	dma_cache_sync(NULL, buf, bytes, DMA_FROM_DEVICE);
+	memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes);
+}
+
+static int hal2_capture_ack(struct snd_pcm_substream *substream)
+{
+	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
+	struct hal2_codec *adc = &hal2->adc;
+
+	snd_pcm_indirect_capture_transfer(substream,
+					  &adc->pcm_indirect,
+					  hal2_capture_transfer);
+	return 0;
+}
+
+static struct snd_pcm_ops hal2_playback_ops = {
+	.open =        hal2_playback_open,
+	.close =       hal2_playback_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   hal2_pcm_hw_params,
+	.hw_free =     hal2_pcm_hw_free,
+	.prepare =     hal2_playback_prepare,
+	.trigger =     hal2_playback_trigger,
+	.pointer =     hal2_playback_pointer,
+	.ack =         hal2_playback_ack,
+};
+
+static struct snd_pcm_ops hal2_capture_ops = {
+	.open =        hal2_capture_open,
+	.close =       hal2_capture_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   hal2_pcm_hw_params,
+	.hw_free =     hal2_pcm_hw_free,
+	.prepare =     hal2_capture_prepare,
+	.trigger =     hal2_capture_trigger,
+	.pointer =     hal2_capture_pointer,
+	.ack =         hal2_capture_ack,
+};
+
+static int __devinit hal2_pcm_create(struct snd_hal2 *hal2)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	/* create first pcm device with one outputs and one input */
+	err = snd_pcm_new(hal2->card, "SGI HAL2 Audio", 0, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = hal2;
+	strcpy(pcm->name, "SGI HAL2");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&hal2_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&hal2_capture_ops);
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					   snd_dma_continuous_data(GFP_KERNEL),
+					   0, 1024 * 1024);
+
+	return 0;
+}
+
+static int hal2_dev_free(struct snd_device *device)
+{
+	struct snd_hal2 *hal2 = device->device_data;
+
+	free_irq(SGI_HPCDMA_IRQ, hal2);
+	kfree(hal2);
+	return 0;
+}
+
+static struct snd_device_ops hal2_ops = {
+	.dev_free = hal2_dev_free,
+};
+
+static void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3,
+			    int index)
+{
+	codec->pbus.pbusnr = index;
+	codec->pbus.pbus = &hpc3->pbdma[index];
+}
+
+static int hal2_detect(struct snd_hal2 *hal2)
+{
+	unsigned short board, major, minor;
+	unsigned short rev;
+
+	/* reset HAL2 */
+	hal2_write(0, &hal2->ctl_regs->isr);
+
+	/* release reset */
+	hal2_write(H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N,
+		   &hal2->ctl_regs->isr);
+
+
+	hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE);
+	rev = hal2_read(&hal2->ctl_regs->rev);
+	if (rev & H2_REV_AUDIO_PRESENT)
+		return -ENODEV;
+
+	board = (rev & H2_REV_BOARD_M) >> 12;
+	major = (rev & H2_REV_MAJOR_CHIP_M) >> 4;
+	minor = (rev & H2_REV_MINOR_CHIP_M);
+
+	printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n",
+	       board, major, minor);
+
+	return 0;
+}
+
+static int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
+{
+	struct snd_hal2 *hal2;
+	struct hpc3_regs *hpc3 = hpc3c0;
+	int err;
+
+	hal2 = kzalloc(sizeof(struct snd_hal2), GFP_KERNEL);
+	if (!hal2)
+		return -ENOMEM;
+
+	hal2->card = card;
+
+	if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
+			"SGI HAL2", hal2)) {
+		printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
+		kfree(hal2);
+		return -EAGAIN;
+	}
+
+	hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0];
+	hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1];
+	hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2];
+	hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3];
+
+	if (hal2_detect(hal2) < 0) {
+		kfree(hal2);
+		return -ENODEV;
+	}
+
+	hal2_init_codec(&hal2->dac, hpc3, 0);
+	hal2_init_codec(&hal2->adc, hpc3, 1);
+
+	/*
+	 * All DMA channel interfaces in HAL2 are designed to operate with
+	 * PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles
+	 * in D5. HAL2 is a 16-bit device which can accept both big and little
+	 * endian format. It assumes that even address bytes are on high
+	 * portion of PBUS (15:8) and assumes that HPC3 is programmed to
+	 * accept a live (unsynchronized) version of P_DREQ_N from HAL2.
+	 */
+#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \
+			  (2 << HPC3_DMACFG_D4R_SHIFT) | \
+			  (2 << HPC3_DMACFG_D5R_SHIFT) | \
+			  (0 << HPC3_DMACFG_D3W_SHIFT) | \
+			  (2 << HPC3_DMACFG_D4W_SHIFT) | \
+			  (2 << HPC3_DMACFG_D5W_SHIFT) | \
+				HPC3_DMACFG_DS16 | \
+				HPC3_DMACFG_EVENHI | \
+				HPC3_DMACFG_RTIME | \
+			  (8 << HPC3_DMACFG_BURST_SHIFT) | \
+				HPC3_DMACFG_DRQLIVE)
+	/*
+	 * Ignore what's mentioned in the specification and write value which
+	 * works in The Real World (TM)
+	 */
+	hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844;
+	hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844;
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hal2, &hal2_ops);
+	if (err < 0) {
+		free_irq(SGI_HPCDMA_IRQ, hal2);
+		kfree(hal2);
+		return err;
+	}
+	*rchip = hal2;
+	return 0;
+}
+
+static int __devinit hal2_probe(struct platform_device *pdev)
+{
+	struct snd_card *card;
+	struct snd_hal2 *chip;
+	int err;
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	err = hal2_create(card, &chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_card_set_dev(card, &pdev->dev);
+
+	err = hal2_pcm_create(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	err = hal2_mixer_create(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "SGI HAL2 Audio");
+	strcpy(card->shortname, "SGI HAL2 Audio");
+	sprintf(card->longname, "%s irq %i",
+		card->shortname,
+		SGI_HPCDMA_IRQ);
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	platform_set_drvdata(pdev, card);
+	return 0;
+}
+
+static int __exit hal2_remove(struct platform_device *pdev)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+
+	snd_card_free(card);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver hal2_driver = {
+	.probe	= hal2_probe,
+	.remove	= __devexit_p(hal2_remove),
+	.driver = {
+		.name	= "sgihal2",
+		.owner	= THIS_MODULE,
+	}
+};
+
+static int __init alsa_card_hal2_init(void)
+{
+	return platform_driver_register(&hal2_driver);
+}
+
+static void __exit alsa_card_hal2_exit(void)
+{
+	platform_driver_unregister(&hal2_driver);
+}
+
+module_init(alsa_card_hal2_init);
+module_exit(alsa_card_hal2_exit);
diff --git a/sound/mips/hal2.h b/sound/mips/hal2.h
new file mode 100644
index 0000000..f19828b
--- /dev/null
+++ b/sound/mips/hal2.h
@@ -0,0 +1,245 @@
+#ifndef __HAL2_H
+#define __HAL2_H
+
+/*
+ *  Driver for HAL2 sound processors
+ *  Copyright (c) 1999 Ulf Carlsson <ulfc@bun.falkenberg.se>
+ *  Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/types.h>
+
+/* Indirect status register */
+
+#define H2_ISR_TSTATUS		0x01	/* RO: transaction status 1=busy */
+#define H2_ISR_USTATUS		0x02	/* RO: utime status bit 1=armed */
+#define H2_ISR_QUAD_MODE	0x04	/* codec mode 0=indigo 1=quad */
+#define H2_ISR_GLOBAL_RESET_N	0x08	/* chip global reset 0=reset */
+#define H2_ISR_CODEC_RESET_N	0x10	/* codec/synth reset 0=reset  */
+
+/* Revision register */
+
+#define H2_REV_AUDIO_PRESENT	0x8000	/* RO: audio present 0=present */
+#define H2_REV_BOARD_M		0x7000	/* RO: bits 14:12, board revision */
+#define H2_REV_MAJOR_CHIP_M	0x00F0	/* RO: bits 7:4, major chip revision */
+#define H2_REV_MINOR_CHIP_M	0x000F	/* RO: bits 3:0, minor chip revision */
+
+/* Indirect address register */
+
+/*
+ * Address of indirect internal register to be accessed. A write to this
+ * register initiates read or write access to the indirect registers in the
+ * HAL2. Note that there af four indirect data registers for write access to
+ * registers larger than 16 byte.
+ */
+
+#define H2_IAR_TYPE_M		0xF000	/* bits 15:12, type of functional */
+					/* block the register resides in */
+					/* 1=DMA Port */
+					/* 9=Global DMA Control */
+					/* 2=Bresenham */
+					/* 3=Unix Timer */
+#define H2_IAR_NUM_M		0x0F00	/* bits 11:8 instance of the */
+					/* blockin which the indirect */
+					/* register resides */
+					/* If IAR_TYPE_M=DMA Port: */
+					/* 1=Synth In */
+					/* 2=AES In */
+					/* 3=AES Out */
+					/* 4=DAC Out */
+					/* 5=ADC Out */
+					/* 6=Synth Control */
+					/* If IAR_TYPE_M=Global DMA Control: */
+					/* 1=Control */
+					/* If IAR_TYPE_M=Bresenham: */
+					/* 1=Bresenham Clock Gen 1 */
+					/* 2=Bresenham Clock Gen 2 */
+					/* 3=Bresenham Clock Gen 3 */
+					/* If IAR_TYPE_M=Unix Timer: */
+					/* 1=Unix Timer */
+#define H2_IAR_ACCESS_SELECT	0x0080	/* 1=read 0=write */
+#define H2_IAR_PARAM		0x000C	/* Parameter Select */
+#define H2_IAR_RB_INDEX_M	0x0003	/* Read Back Index */
+					/* 00:word0 */
+					/* 01:word1 */
+					/* 10:word2 */
+					/* 11:word3 */
+/*
+ * HAL2 internal addressing
+ *
+ * The HAL2 has "indirect registers" (idr) which are accessed by writing to the
+ * Indirect Data registers. Write the address to the Indirect Address register
+ * to transfer the data.
+ *
+ * We define the H2IR_* to the read address and H2IW_* to the write address and
+ * H2I_* to be fields in whatever register is referred to.
+ *
+ * When we write to indirect registers which are larger than one word (16 bit)
+ * we have to fill more than one indirect register before writing. When we read
+ * back however we have to read several times, each time with different Read
+ * Back Indexes (there are defs for doing this easily).
+ */
+
+/*
+ * Relay Control
+ */
+#define H2I_RELAY_C		0x9100
+#define H2I_RELAY_C_STATE	0x01		/* state of RELAY pin signal */
+
+/* DMA port enable */
+
+#define H2I_DMA_PORT_EN		0x9104
+#define H2I_DMA_PORT_EN_SY_IN	0x01		/* Synth_in DMA port */
+#define H2I_DMA_PORT_EN_AESRX	0x02		/* AES receiver DMA port */
+#define H2I_DMA_PORT_EN_AESTX	0x04		/* AES transmitter DMA port */
+#define H2I_DMA_PORT_EN_CODECTX	0x08		/* CODEC transmit DMA port */
+#define H2I_DMA_PORT_EN_CODECR	0x10		/* CODEC receive DMA port */
+
+#define H2I_DMA_END		0x9108 		/* global dma endian select */
+#define H2I_DMA_END_SY_IN	0x01		/* Synth_in DMA port */
+#define H2I_DMA_END_AESRX	0x02		/* AES receiver DMA port */
+#define H2I_DMA_END_AESTX	0x04		/* AES transmitter DMA port */
+#define H2I_DMA_END_CODECTX	0x08		/* CODEC transmit DMA port */
+#define H2I_DMA_END_CODECR	0x10		/* CODEC receive DMA port */
+						/* 0=b_end 1=l_end */
+
+#define H2I_DMA_DRV		0x910C  	/* global PBUS DMA enable */
+
+#define H2I_SYNTH_C		0x1104		/* Synth DMA control */
+
+#define H2I_AESRX_C		0x1204	 	/* AES RX dma control */
+
+#define H2I_C_TS_EN		0x20		/* Timestamp enable */
+#define H2I_C_TS_FRMT		0x40		/* Timestamp format */
+#define H2I_C_NAUDIO		0x80		/* Sign extend */
+
+/* AESRX CTL, 16 bit */
+
+#define H2I_AESTX_C		0x1304		/* AES TX DMA control */
+#define H2I_AESTX_C_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */
+#define H2I_AESTX_C_CLKID_M	0x18
+#define H2I_AESTX_C_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */
+#define H2I_AESTX_C_DATAT_M	0x300
+
+/* CODEC registers */
+
+#define H2I_DAC_C1		0x1404 		/* DAC DMA control, 16 bit */
+#define H2I_DAC_C2		0x1408		/* DAC DMA control, 32 bit */
+#define H2I_ADC_C1		0x1504 		/* ADC DMA control, 16 bit */
+#define H2I_ADC_C2		0x1508		/* ADC DMA control, 32 bit */
+
+/* Bits in CTL1 register */
+
+#define H2I_C1_DMA_SHIFT	0		/* DMA channel */
+#define H2I_C1_DMA_M		0x7
+#define H2I_C1_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */
+#define H2I_C1_CLKID_M		0x18
+#define H2I_C1_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */
+#define H2I_C1_DATAT_M		0x300
+
+/* Bits in CTL2 register */
+
+#define H2I_C2_R_GAIN_SHIFT	0		/* right a/d input gain */
+#define H2I_C2_R_GAIN_M		0xf
+#define H2I_C2_L_GAIN_SHIFT	4		/* left a/d input gain */
+#define H2I_C2_L_GAIN_M		0xf0
+#define H2I_C2_R_SEL		0x100		/* right input select */
+#define H2I_C2_L_SEL		0x200		/* left input select */
+#define H2I_C2_MUTE		0x400		/* mute */
+#define H2I_C2_DO1		0x00010000	/* digital output port bit 0 */
+#define H2I_C2_DO2		0x00020000	/* digital output port bit 1 */
+#define H2I_C2_R_ATT_SHIFT	18		/* right d/a output - */
+#define H2I_C2_R_ATT_M		0x007c0000	/* attenuation */
+#define H2I_C2_L_ATT_SHIFT	23		/* left d/a output - */
+#define H2I_C2_L_ATT_M		0x0f800000	/* attenuation */
+
+#define H2I_SYNTH_MAP_C		0x1104		/* synth dma handshake ctrl */
+
+/* Clock generator CTL 1, 16 bit */
+
+#define H2I_BRES1_C1		0x2104
+#define H2I_BRES2_C1		0x2204
+#define H2I_BRES3_C1		0x2304
+
+#define H2I_BRES_C1_SHIFT	0		/* 0=48.0 1=44.1 2=aes_rx */
+#define H2I_BRES_C1_M		0x03
+
+/* Clock generator CTL 2, 32 bit */
+
+#define H2I_BRES1_C2		0x2108
+#define H2I_BRES2_C2		0x2208
+#define H2I_BRES3_C2		0x2308
+
+#define H2I_BRES_C2_INC_SHIFT	0		/* increment value */
+#define H2I_BRES_C2_INC_M	0xffff
+#define H2I_BRES_C2_MOD_SHIFT	16		/* modcontrol value */
+#define H2I_BRES_C2_MOD_M	0xffff0000	/* modctrl=0xffff&(modinc-1) */
+
+/* Unix timer, 64 bit */
+
+#define H2I_UTIME		0x3104
+#define H2I_UTIME_0_LD		0xffff		/* microseconds, LSB's */
+#define H2I_UTIME_1_LD0		0x0f		/* microseconds, MSB's */
+#define H2I_UTIME_1_LD1		0xf0		/* tenths of microseconds */
+#define H2I_UTIME_2_LD		0xffff		/* seconds, LSB's */
+#define H2I_UTIME_3_LD		0xffff		/* seconds, MSB's */
+
+struct hal2_ctl_regs {
+	u32 _unused0[4];
+	u32 isr;		/* 0x10 Status Register */
+	u32 _unused1[3];
+	u32 rev;		/* 0x20 Revision Register */
+	u32 _unused2[3];
+	u32 iar;		/* 0x30 Indirect Address Register */
+	u32 _unused3[3];
+	u32 idr0;		/* 0x40 Indirect Data Register 0 */
+	u32 _unused4[3];
+	u32 idr1;		/* 0x50 Indirect Data Register 1 */
+	u32 _unused5[3];
+	u32 idr2;		/* 0x60 Indirect Data Register 2 */
+	u32 _unused6[3];
+	u32 idr3;		/* 0x70 Indirect Data Register 3 */
+};
+
+struct hal2_aes_regs {
+	u32 rx_stat[2];	/* Status registers */
+	u32 rx_cr[2];		/* Control registers */
+	u32 rx_ud[4];		/* User data window */
+	u32 rx_st[24];		/* Channel status data */
+
+	u32 tx_stat[1];	/* Status register */
+	u32 tx_cr[3];		/* Control registers */
+	u32 tx_ud[4];		/* User data window */
+	u32 tx_st[24];		/* Channel status data */
+};
+
+struct hal2_vol_regs {
+	u32 right;		/* Right volume */
+	u32 left;		/* Left volume */
+};
+
+struct hal2_syn_regs {
+	u32 _unused0[2];
+	u32 page;		/* DOC Page register */
+	u32 regsel;		/* DOC Register selection */
+	u32 dlow;		/* DOC Data low */
+	u32 dhigh;		/* DOC Data high */
+	u32 irq;		/* IRQ Status */
+	u32 dram;		/* DRAM Access */
+};
+
+#endif	/* __HAL2_H */
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
new file mode 100644
index 0000000..4c63504
--- /dev/null
+++ b/sound/mips/sgio2audio.c
@@ -0,0 +1,1006 @@
+/*
+ *   Sound driver for Silicon Graphics O2 Workstations A/V board audio.
+ *
+ *   Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
+ *   Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ *   Mxier part taken from mace_audio.c:
+ *   Copyright 2007 Thorben Jändling <tj.trevelyan@gmail.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/gfp.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/ip32/ip32_ints.h>
+#include <asm/ip32/mace.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+#include <sound/ad1843.h>
+
+
+MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org>");
+MODULE_DESCRIPTION("SGI O2 Audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}");
+
+static int index = SNDRV_DEFAULT_IDX1;  /* Index 0-MAX */
+static char *id = SNDRV_DEFAULT_STR1;   /* ID for this card */
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SGI O2 soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SGI O2 soundcard.");
+
+
+#define AUDIO_CONTROL_RESET              BIT(0) /* 1: reset audio interface */
+#define AUDIO_CONTROL_CODEC_PRESENT      BIT(1) /* 1: codec detected */
+
+#define CODEC_CONTROL_WORD_SHIFT        0
+#define CODEC_CONTROL_READ              BIT(16)
+#define CODEC_CONTROL_ADDRESS_SHIFT     17
+
+#define CHANNEL_CONTROL_RESET           BIT(10) /* 1: reset channel */
+#define CHANNEL_DMA_ENABLE              BIT(9)  /* 1: enable DMA transfer */
+#define CHANNEL_INT_THRESHOLD_DISABLED  (0 << 5) /* interrupt disabled */
+#define CHANNEL_INT_THRESHOLD_25        (1 << 5) /* int on buffer >25% full */
+#define CHANNEL_INT_THRESHOLD_50        (2 << 5) /* int on buffer >50% full */
+#define CHANNEL_INT_THRESHOLD_75        (3 << 5) /* int on buffer >75% full */
+#define CHANNEL_INT_THRESHOLD_EMPTY     (4 << 5) /* int on buffer empty */
+#define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
+#define CHANNEL_INT_THRESHOLD_FULL      (6 << 5) /* int on buffer empty */
+#define CHANNEL_INT_THRESHOLD_NOT_FULL  (7 << 5) /* int on buffer !empty */
+
+#define CHANNEL_RING_SHIFT              12
+#define CHANNEL_RING_SIZE               (1 << CHANNEL_RING_SHIFT)
+#define CHANNEL_RING_MASK               (CHANNEL_RING_SIZE - 1)
+
+#define CHANNEL_LEFT_SHIFT 40
+#define CHANNEL_RIGHT_SHIFT 8
+
+struct snd_sgio2audio_chan {
+	int idx;
+	struct snd_pcm_substream *substream;
+	int pos;
+	snd_pcm_uframes_t size;
+	spinlock_t lock;
+};
+
+/* definition of the chip-specific record */
+struct snd_sgio2audio {
+	struct snd_card *card;
+
+	/* codec */
+	struct snd_ad1843 ad1843;
+	spinlock_t ad1843_lock;
+
+	/* channels */
+	struct snd_sgio2audio_chan channel[3];
+
+	/* resources */
+	void *ring_base;
+	dma_addr_t ring_base_dma;
+};
+
+/* AD1843 access */
+
+/*
+ * read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
+ *
+ * Returns unsigned register value on success, -errno on failure.
+ */
+static int read_ad1843_reg(void *priv, int reg)
+{
+	struct snd_sgio2audio *chip = priv;
+	int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
+	       CODEC_CONTROL_READ, &mace->perif.audio.codec_control);
+	wmb();
+	val = readq(&mace->perif.audio.codec_control); /* flush bus */
+	udelay(200);
+
+	val = readq(&mace->perif.audio.codec_read);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+	return val;
+}
+
+/*
+ * write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
+ */
+static int write_ad1843_reg(void *priv, int reg, int word)
+{
+	struct snd_sgio2audio *chip = priv;
+	int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->ad1843_lock, flags);
+
+	writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
+	       (word << CODEC_CONTROL_WORD_SHIFT),
+	       &mace->perif.audio.codec_control);
+	wmb();
+	val = readq(&mace->perif.audio.codec_control); /* flush bus */
+	udelay(200);
+
+	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
+	return 0;
+}
+
+static int sgio2audio_gain_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843,
+					     (int)kcontrol->private_value);
+	return 0;
+}
+
+static int sgio2audio_gain_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+	int vol;
+
+	vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value);
+
+	ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF;
+	ucontrol->value.integer.value[1] = vol & 0xFF;
+
+	return 0;
+}
+
+static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+	int newvol, oldvol;
+
+	oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value);
+	newvol = (ucontrol->value.integer.value[0] << 8) |
+		ucontrol->value.integer.value[1];
+
+	newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value,
+		newvol);
+
+	return newvol != oldvol;
+}
+
+static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	static const char *texts[3] = {
+		"Cam Mic", "Mic", "Line"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item >= 3)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843);
+	return 0;
+}
+
+static int sgio2audio_source_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
+	int newsrc, oldsrc;
+
+	oldsrc = ad1843_get_recsrc(&chip->ad1843);
+	newsrc = ad1843_set_recsrc(&chip->ad1843,
+				   ucontrol->value.enumerated.item[0]);
+
+	return newsrc != oldsrc;
+}
+
+/* dac1/pcm0 mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "PCM Playback Volume",
+	.index          = 0,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_PCM_0,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* dac2/pcm1 mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "PCM Playback Volume",
+	.index          = 1,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_PCM_1,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* record level mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Capture Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_RECLEV,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* record level source control */
+static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Capture Source",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info           = sgio2audio_source_info,
+	.get            = sgio2audio_source_get,
+	.put            = sgio2audio_source_put,
+};
+
+/* line mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Line Playback Volume",
+	.index          = 0,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_LINE,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* cd mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Line Playback Volume",
+	.index          = 1,
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_LINE_2,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+/* mic mixer control */
+static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
+	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name           = "Mic Playback Volume",
+	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value  = AD1843_GAIN_MIC,
+	.info           = sgio2audio_gain_info,
+	.get            = sgio2audio_gain_get,
+	.put            = sgio2audio_gain_put,
+};
+
+
+static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
+{
+	int err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_recsource, chip));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_line, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_cd, chip));
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(chip->card,
+			  snd_ctl_new1(&sgio2audio_ctrl_mic, chip));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+/* low-level audio interface DMA */
+
+/* get data out of bounce buffer, count must be a multiple of 32 */
+/* returns 1 if a period has elapsed */
+static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
+					unsigned int ch, unsigned int count)
+{
+	int ret;
+	unsigned long src_base, src_pos, dst_mask;
+	unsigned char *dst_base;
+	int dst_pos;
+	u64 *src;
+	s16 *dst;
+	u64 x;
+	unsigned long flags;
+	struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
+
+	spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+	src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT);
+	src_pos = readq(&mace->perif.audio.chan[ch].read_ptr);
+	dst_base = runtime->dma_area;
+	dst_pos = chip->channel[ch].pos;
+	dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
+
+	/* check if a period has elapsed */
+	chip->channel[ch].size += (count >> 3); /* in frames */
+	ret = chip->channel[ch].size >= runtime->period_size;
+	chip->channel[ch].size %= runtime->period_size;
+
+	while (count) {
+		src = (u64 *)(src_base + src_pos);
+		dst = (s16 *)(dst_base + dst_pos);
+
+		x = *src;
+		dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff;
+		dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff;
+
+		src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK;
+		dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask;
+		count -= sizeof(u64);
+	}
+
+	writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */
+	chip->channel[ch].pos = dst_pos;
+
+	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+	return ret;
+}
+
+/* put some DMA data in bounce buffer, count must be a multiple of 32 */
+/* returns 1 if a period has elapsed */
+static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
+					unsigned int ch, unsigned int count)
+{
+	int ret;
+	s64 l, r;
+	unsigned long dst_base, dst_pos, src_mask;
+	unsigned char *src_base;
+	int src_pos;
+	u64 *dst;
+	s16 *src;
+	unsigned long flags;
+	struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
+
+	spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+	dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT);
+	dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr);
+	src_base = runtime->dma_area;
+	src_pos = chip->channel[ch].pos;
+	src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
+
+	/* check if a period has elapsed */
+	chip->channel[ch].size += (count >> 3); /* in frames */
+	ret = chip->channel[ch].size >= runtime->period_size;
+	chip->channel[ch].size %= runtime->period_size;
+
+	while (count) {
+		src = (s16 *)(src_base + src_pos);
+		dst = (u64 *)(dst_base + dst_pos);
+
+		l = src[0]; /* sign extend */
+		r = src[1]; /* sign extend */
+
+		*dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) |
+			((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT);
+
+		dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK;
+		src_pos = (src_pos + 2 * sizeof(s16)) & src_mask;
+		count -= sizeof(u64);
+	}
+
+	writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */
+	chip->channel[ch].pos = src_pos;
+
+	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+	return ret;
+}
+
+static int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+	int ch = chan->idx;
+
+	/* reset DMA channel */
+	writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control);
+	udelay(10);
+	writeq(0, &mace->perif.audio.chan[ch].control);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* push a full buffer */
+		snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32);
+	}
+	/* set DMA to wake on 50% empty and enable interrupt */
+	writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50,
+	       &mace->perif.audio.chan[ch].control);
+	return 0;
+}
+
+static int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+
+	writeq(0, &mace->perif.audio.chan[chan->idx].control);
+	return 0;
+}
+
+static irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id)
+{
+	struct snd_sgio2audio_chan *chan = dev_id;
+	struct snd_pcm_substream *substream;
+	struct snd_sgio2audio *chip;
+	int count, ch;
+
+	substream = chan->substream;
+	chip = snd_pcm_substream_chip(substream);
+	ch = chan->idx;
+
+	/* empty the ring */
+	count = CHANNEL_RING_SIZE -
+		readq(&mace->perif.audio.chan[ch].depth) - 32;
+	if (snd_sgio2audio_dma_pull_frag(chip, ch, count))
+		snd_pcm_period_elapsed(substream);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id)
+{
+	struct snd_sgio2audio_chan *chan = dev_id;
+	struct snd_pcm_substream *substream;
+	struct snd_sgio2audio *chip;
+	int count, ch;
+
+	substream = chan->substream;
+	chip = snd_pcm_substream_chip(substream);
+	ch = chan->idx;
+	/* fill the ring */
+	count = CHANNEL_RING_SIZE -
+		readq(&mace->perif.audio.chan[ch].depth) - 32;
+	if (snd_sgio2audio_dma_push_frag(chip, ch, count))
+		snd_pcm_period_elapsed(substream);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id)
+{
+	struct snd_sgio2audio_chan *chan = dev_id;
+	struct snd_pcm_substream *substream;
+
+	substream = chan->substream;
+	snd_sgio2audio_dma_stop(substream);
+	snd_sgio2audio_dma_start(substream);
+	return IRQ_HANDLED;
+}
+
+/* PCM part */
+/* PCM hardware definition */
+static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
+	.rates =            SNDRV_PCM_RATE_8000_48000,
+	.rate_min =         8000,
+	.rate_max =         48000,
+	.channels_min =     2,
+	.channels_max =     2,
+	.buffer_bytes_max = 65536,
+	.period_bytes_min = 32768,
+	.period_bytes_max = 65536,
+	.periods_min =      1,
+	.periods_max =      1024,
+};
+
+/* PCM playback open callback */
+static int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sgio2audio_pcm_hw;
+	runtime->private_data = &chip->channel[1];
+	return 0;
+}
+
+static int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sgio2audio_pcm_hw;
+	runtime->private_data = &chip->channel[2];
+	return 0;
+}
+
+/* PCM capture open callback */
+static int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sgio2audio_pcm_hw;
+	runtime->private_data = &chip->channel[0];
+	return 0;
+}
+
+/* PCM close callback */
+static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->private_data = NULL;
+	return 0;
+}
+
+
+/* hw_params callback */
+static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int size = params_buffer_bytes(hw_params);
+
+	/* alloc virtual 'dma' area */
+	if (runtime->dma_area)
+		vfree(runtime->dma_area);
+	runtime->dma_area = vmalloc(size);
+	if (runtime->dma_area == NULL)
+		return -ENOMEM;
+	runtime->dma_bytes = size;
+	return 0;
+}
+
+/* hw_free callback */
+static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	if (substream->runtime->dma_area)
+		vfree(substream->runtime->dma_area);
+	substream->runtime->dma_area = NULL;
+	return 0;
+}
+
+/* prepare callback */
+static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+	int ch = chan->idx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->channel[ch].lock, flags);
+
+	/* Setup the pseudo-dma transfer pointers.  */
+	chip->channel[ch].pos = 0;
+	chip->channel[ch].size = 0;
+	chip->channel[ch].substream = substream;
+
+	/* set AD1843 format */
+	/* hardware format is always S16_LE */
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ad1843_setup_dac(&chip->ad1843,
+				 ch - 1,
+				 runtime->rate,
+				 SNDRV_PCM_FORMAT_S16_LE,
+				 runtime->channels);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ad1843_setup_adc(&chip->ad1843,
+				 runtime->rate,
+				 SNDRV_PCM_FORMAT_S16_LE,
+				 runtime->channels);
+		break;
+	}
+	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
+	return 0;
+}
+
+/* trigger callback */
+static int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream,
+				      int cmd)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* start the PCM engine */
+		snd_sgio2audio_dma_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		/* stop the PCM engine */
+		snd_sgio2audio_dma_stop(substream);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
+	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
+
+	/* get the current hardware pointer */
+	return bytes_to_frames(substream->runtime,
+			       chip->channel[chan->idx].pos);
+}
+
+/* get the physical page pointer on the given offset */
+static struct page *snd_sgio2audio_page(struct snd_pcm_substream *substream,
+					unsigned long offset)
+{
+	return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+
+/* operators */
+static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
+	.open =        snd_sgio2audio_playback1_open,
+	.close =       snd_sgio2audio_pcm_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_sgio2audio_pcm_hw_params,
+	.hw_free =     snd_sgio2audio_pcm_hw_free,
+	.prepare =     snd_sgio2audio_pcm_prepare,
+	.trigger =     snd_sgio2audio_pcm_trigger,
+	.pointer =     snd_sgio2audio_pcm_pointer,
+	.page =        snd_sgio2audio_page,
+};
+
+static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
+	.open =        snd_sgio2audio_playback2_open,
+	.close =       snd_sgio2audio_pcm_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_sgio2audio_pcm_hw_params,
+	.hw_free =     snd_sgio2audio_pcm_hw_free,
+	.prepare =     snd_sgio2audio_pcm_prepare,
+	.trigger =     snd_sgio2audio_pcm_trigger,
+	.pointer =     snd_sgio2audio_pcm_pointer,
+	.page =        snd_sgio2audio_page,
+};
+
+static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
+	.open =        snd_sgio2audio_capture_open,
+	.close =       snd_sgio2audio_pcm_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_sgio2audio_pcm_hw_params,
+	.hw_free =     snd_sgio2audio_pcm_hw_free,
+	.prepare =     snd_sgio2audio_pcm_prepare,
+	.trigger =     snd_sgio2audio_pcm_trigger,
+	.pointer =     snd_sgio2audio_pcm_pointer,
+	.page =        snd_sgio2audio_page,
+};
+
+/*
+ *  definitions of capture are omitted here...
+ */
+
+/* create a pcm device */
+static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	/* create first pcm device with one outputs and one input */
+	err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SGI O2 DAC1");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_sgio2audio_playback1_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&snd_sgio2audio_capture_ops);
+
+	/* create second  pcm device with one outputs and no input */
+	err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SGI O2 DAC2");
+
+	/* set operators */
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_sgio2audio_playback2_ops);
+
+	return 0;
+}
+
+static struct {
+	int idx;
+	int irq;
+	irqreturn_t (*isr)(int, void *);
+	const char *desc;
+} snd_sgio2_isr_table[] = {
+	{
+		.idx = 0,
+		.irq = MACEISA_AUDIO1_DMAT_IRQ,
+		.isr = snd_sgio2audio_dma_in_isr,
+		.desc = "Capture DMA Channel 0"
+	}, {
+		.idx = 0,
+		.irq = MACEISA_AUDIO1_OF_IRQ,
+		.isr = snd_sgio2audio_error_isr,
+		.desc = "Capture Overflow"
+	}, {
+		.idx = 1,
+		.irq = MACEISA_AUDIO2_DMAT_IRQ,
+		.isr = snd_sgio2audio_dma_out_isr,
+		.desc = "Playback DMA Channel 1"
+	}, {
+		.idx = 1,
+		.irq = MACEISA_AUDIO2_MERR_IRQ,
+		.isr = snd_sgio2audio_error_isr,
+		.desc = "Memory Error Channel 1"
+	}, {
+		.idx = 2,
+		.irq = MACEISA_AUDIO3_DMAT_IRQ,
+		.isr = snd_sgio2audio_dma_out_isr,
+		.desc = "Playback DMA Channel 2"
+	}, {
+		.idx = 2,
+		.irq = MACEISA_AUDIO3_MERR_IRQ,
+		.isr = snd_sgio2audio_error_isr,
+		.desc = "Memory Error Channel 2"
+	}
+};
+
+/* ALSA driver */
+
+static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
+{
+	int i;
+
+	/* reset interface */
+	writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
+	udelay(1);
+	writeq(0, &mace->perif.audio.control);
+
+	/* release IRQ's */
+	for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++)
+		free_irq(snd_sgio2_isr_table[i].irq,
+			 &chip->channel[snd_sgio2_isr_table[i].idx]);
+
+	dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+			  chip->ring_base, chip->ring_base_dma);
+
+	/* release card data */
+	kfree(chip);
+	return 0;
+}
+
+static int snd_sgio2audio_dev_free(struct snd_device *device)
+{
+	struct snd_sgio2audio *chip = device->device_data;
+
+	return snd_sgio2audio_free(chip);
+}
+
+static struct snd_device_ops ops = {
+	.dev_free = snd_sgio2audio_dev_free,
+};
+
+static int __devinit snd_sgio2audio_create(struct snd_card *card,
+					   struct snd_sgio2audio **rchip)
+{
+	struct snd_sgio2audio *chip;
+	int i, err;
+
+	*rchip = NULL;
+
+	/* check if a codec is attached to the interface */
+	/* (Audio or Audio/Video board present) */
+	if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT))
+		return -ENOENT;
+
+	chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+					     &chip->ring_base_dma, GFP_USER);
+	if (chip->ring_base == NULL) {
+		printk(KERN_ERR
+		       "sgio2audio: could not allocate ring buffers\n");
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&chip->ad1843_lock);
+
+	/* initialize channels */
+	for (i = 0; i < 3; i++) {
+		spin_lock_init(&chip->channel[i].lock);
+		chip->channel[i].idx = i;
+	}
+
+	/* allocate IRQs */
+	for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) {
+		if (request_irq(snd_sgio2_isr_table[i].irq,
+				snd_sgio2_isr_table[i].isr,
+				0,
+				snd_sgio2_isr_table[i].desc,
+				&chip->channel[snd_sgio2_isr_table[i].idx])) {
+			snd_sgio2audio_free(chip);
+			printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n",
+			       snd_sgio2_isr_table[i].irq);
+			return -EBUSY;
+		}
+	}
+
+	/* reset the interface */
+	writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
+	udelay(1);
+	writeq(0, &mace->perif.audio.control);
+	msleep_interruptible(1); /* give time to recover */
+
+	/* set ring base */
+	writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase);
+
+	/* attach the AD1843 codec */
+	chip->ad1843.read = read_ad1843_reg;
+	chip->ad1843.write = write_ad1843_reg;
+	chip->ad1843.chip = chip;
+
+	/* initialize the AD1843 codec */
+	err = ad1843_init(&chip->ad1843);
+	if (err < 0) {
+		snd_sgio2audio_free(chip);
+		return err;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sgio2audio_free(chip);
+		return err;
+	}
+	*rchip = chip;
+	return 0;
+}
+
+static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
+{
+	struct snd_card *card;
+	struct snd_sgio2audio *chip;
+	int err;
+
+	card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	err = snd_sgio2audio_create(card, &chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	snd_card_set_dev(card, &pdev->dev);
+
+	err = snd_sgio2audio_new_pcm(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	err = snd_sgio2audio_new_mixer(chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "SGI O2 Audio");
+	strcpy(card->shortname, "SGI O2 Audio");
+	sprintf(card->longname, "%s irq %i-%i",
+		card->shortname,
+		MACEISA_AUDIO1_DMAT_IRQ,
+		MACEISA_AUDIO3_MERR_IRQ);
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	platform_set_drvdata(pdev, card);
+	return 0;
+}
+
+static int __exit snd_sgio2audio_remove(struct platform_device *pdev)
+{
+	struct snd_card *card = platform_get_drvdata(pdev);
+
+	snd_card_free(card);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver sgio2audio_driver = {
+	.probe	= snd_sgio2audio_probe,
+	.remove	= __devexit_p(snd_sgio2audio_remove),
+	.driver = {
+		.name	= "sgio2audio",
+		.owner	= THIS_MODULE,
+	}
+};
+
+static int __init alsa_card_sgio2audio_init(void)
+{
+	return platform_driver_register(&sgio2audio_driver);
+}
+
+static void __exit alsa_card_sgio2audio_exit(void)
+{
+	platform_driver_unregister(&sgio2audio_driver);
+}
+
+module_init(alsa_card_sgio2audio_init)
+module_exit(alsa_card_sgio2audio_exit)
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index 3be2dc1..3394013 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -7,7 +7,7 @@
 
 config SOUND_BCM_CS4297A
 	tristate "Crystal Sound CS4297a (for Swarm)"
-	depends on SOUND_PRIME && SIBYTE_SWARM
+	depends on SIBYTE_SWARM
 	help
 	  The BCM91250A has a Crystal CS4297a on synchronous serial
 	  port B (in addition to the DB-9 serial port).  Say Y or M
@@ -17,7 +17,7 @@
 
 config SOUND_VWSND
 	tristate "SGI Visual Workstation Sound"
-	depends on SOUND_PRIME && X86_VISWS
+	depends on X86_VISWS
 	help
 	  Say Y or M if you have an SGI Visual Workstation and you want to be
 	  able to use its on-board audio.  Read
@@ -26,19 +26,18 @@
 
 config SOUND_HAL2
 	tristate "SGI HAL2 sound (EXPERIMENTAL)"
-	depends on SOUND_PRIME && SGI_IP22 && EXPERIMENTAL
+	depends on SGI_IP22 && EXPERIMENTAL
 	help
 	  Say Y or M if you have an SGI Indy or Indigo2 system and want to be able to
 	  use its on-board A2 audio system.
 
 config SOUND_AU1550_AC97
 	tristate "Au1550/Au1200 AC97 Sound"
-	select SND_AC97_CODEC
-	depends on SOUND_PRIME && (SOC_AU1550 || SOC_AU1200)
+	depends on SOC_AU1550 || SOC_AU1200
 
 config SOUND_TRIDENT
 	tristate "Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core"
-	depends on SOUND_PRIME && PCI
+	depends on PCI
 	---help---
 	  Say Y or M if you have a PCI sound card utilizing the Trident
 	  4DWave-DX/NX chipset or your mother board chipset has SiS 7018
@@ -79,7 +78,7 @@
 
 config SOUND_MSNDCLAS
 	tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
-	depends on SOUND_PRIME && (m || !STANDALONE) && ISA
+	depends on (m || !STANDALONE) && ISA
 	help
 	  Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
 	  Monterey (not for the Pinnacle or Fiji).
@@ -143,7 +142,7 @@
 
 config SOUND_MSNDPIN
 	tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji"
-	depends on SOUND_PRIME && (m || !STANDALONE) && ISA
+	depends on (m || !STANDALONE) && ISA
 	help
 	  Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji.
 	  See <file:Documentation/sound/oss/MultiSound> for important information
@@ -229,7 +228,7 @@
 	  configure the card's resources.
 
 comment "MSND Pinnacle DSP section will be configured to above parameters."
-	depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+	depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
 
 config MSNDPIN_CFG
 	hex "MSND Pinnacle config port 250,260,270"
@@ -242,7 +241,7 @@
 	  Mode".
 
 comment "Pinnacle-specific Device Configuration (0 disables)"
-	depends on SOUND_PRIME && SOUND_MSNDPIN=y && MSNDPIN_NONPNP
+	depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
 
 config MSNDPIN_MPU_IO
 	hex "MSND Pinnacle MPU I/O (e.g. 330)"
@@ -294,7 +293,7 @@
 
 config MSND_FIFOSIZE
 	int "MSND buffer size (kB)"
-	depends on SOUND_PRIME && (SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y)
+	depends on SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y
 	default "128"
 	help
 	  Configures the size of each audio buffer, in kilobytes, for
@@ -302,9 +301,9 @@
 	  and Pinnacle). Larger values reduce the chance of data overruns at
 	  the expense of overall latency. If unsure, use the default.
 
-config SOUND_OSS
+menuconfig SOUND_OSS
 	tristate "OSS sound modules"
-	depends on SOUND_PRIME && ISA_DMA_API && VIRT_TO_BUS
+	depends on ISA_DMA_API && VIRT_TO_BUS
 	help
 	  OSS is the Open Sound System suite of sound card drivers.  They make
 	  sound programming easier since they provide a common API.  Say Y or
@@ -312,16 +311,16 @@
 	  driver for your sound card above, then pick your driver from the
 	  list below.
 
+if SOUND_OSS
+
 config SOUND_TRACEINIT
 	bool "Verbose initialisation"
-	depends on SOUND_OSS
 	help
 	  Verbose soundcard initialization -- affects the format of autoprobe
 	  and initialization messages at boot time.
 
 config SOUND_DMAP
 	bool "Persistent DMA buffers"
-	depends on SOUND_OSS
 	---help---
 	  Linux can often have problems allocating DMA buffers for ISA sound
 	  cards on machines with more than 16MB of RAM. This is because ISA
@@ -338,8 +337,6 @@
 
 config SOUND_SSCAPE
 	tristate "Ensoniq SoundScape support"
-	depends on SOUND_OSS
-	depends on VIRT_TO_BUS
 	help
 	  Answer Y if you have a sound card based on the Ensoniq SoundScape
 	  chipset. Such cards are being manufactured at least by Ensoniq, Spea
@@ -352,13 +349,11 @@
 
 config SOUND_VMIDI
 	tristate "Loopback MIDI device support"
-	depends on SOUND_OSS
 	help
 	  Support for MIDI loopback on port 1 or 2.
 
 config SOUND_TRIX
 	tristate "MediaTrix AudioTrix Pro support"
-	depends on SOUND_OSS
 	help
 	  Answer Y if you have the AudioTriX Pro sound card manufactured
 	  by MediaTrix.
@@ -382,7 +377,6 @@
 
 config SOUND_MSS
 	tristate "Microsoft Sound System support"
-	depends on SOUND_OSS
 	---help---
 	  Again think carefully before answering Y to this question.  It's
 	  safe to answer Y if you have the original Windows Sound System card
@@ -414,7 +408,6 @@
 
 config SOUND_MPU401
 	tristate "MPU-401 support (NOT for SB16)"
-	depends on SOUND_OSS
 	---help---
 	  Be careful with this question.  The MPU401 interface is supported by
 	  all sound cards.  However, some natively supported cards have their
@@ -430,7 +423,6 @@
 
 config SOUND_PAS
 	tristate "ProAudioSpectrum 16 support"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
 	  16 or Logitech SoundMan 16 sound card. Answer N if you have some
@@ -452,7 +444,6 @@
 
 config SOUND_PSS
 	tristate "PSS (AD1848, ADSP-2115, ESC614) support"
-	depends on SOUND_OSS
 	help
 	  Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
 	  ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
@@ -495,7 +486,6 @@
 
 config SOUND_SB
 	tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y if you have an original Sound Blaster card made by Creative
 	  Labs or a 100% hardware compatible clone (like the Thunderboard or
@@ -522,7 +512,6 @@
 
 config SOUND_YM3812
 	tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
 	  Answering Y is usually a safe and recommended choice, however some
@@ -538,7 +527,6 @@
 
 config SOUND_UART6850
 	tristate "6850 UART support"
-	depends on SOUND_OSS
 	help
 	  This option enables support for MIDI interfaces based on the 6850
 	  UART chip. This interface is rarely found on sound cards. It's safe
@@ -549,7 +537,6 @@
 
 config SOUND_AEDSP16
 	tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)"
-	depends on SOUND_OSS
 	---help---
 	  Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
 	  driver supports Audio Excel DSP 16 but not the III nor PnP versions
@@ -630,14 +617,14 @@
 
 config SOUND_VIDC
 	tristate "VIDC 16-bit sound"
-	depends on ARM && (ARCH_ACORN || ARCH_CLPS7500) && SOUND_OSS
+	depends on ARM && (ARCH_ACORN || ARCH_CLPS7500)
 	help
 	  16-bit support for the VIDC onboard sound hardware found on Acorn
 	  machines.
 
 config SOUND_WAVEARTIST
 	tristate "Netwinder WaveArtist"
-	depends on ARM && SOUND_OSS && ARCH_NETWINDER
+	depends on ARM && ARCH_NETWINDER
 	help
 	  Say Y here to include support for the Rockwell WaveArtist sound
 	  system.  This driver is mainly for the NetWinder.
@@ -646,9 +633,11 @@
 	tristate "XpressAudio Sound Blaster emulation"
 	depends on SOUND_SB
 
+endif	# SOUND_OSS
+
 config SOUND_SH_DAC_AUDIO
 	tristate "SuperH DAC audio support"
-	depends on SOUND_PRIME && CPU_SH3
+	depends on CPU_SH3
 
 config SOUND_SH_DAC_AUDIO_CHANNEL
 	int "DAC channel"
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index a003c0e..95fc5c6 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -211,10 +211,6 @@
 static int irq_installed;
 #endif /* MODULE */
 
-/* software implemented recording volume! */
-uint software_input_volume = SW_INPUT_VOLUME_SCALE * SW_INPUT_VOLUME_DEFAULT;
-EXPORT_SYMBOL(software_input_volume);
-
 /* control over who can modify resources shared between play/record */
 static mode_t shared_resource_owner;
 static int shared_resources_initialised;
@@ -1188,7 +1184,7 @@
 
 /* publish this function for use by low-level code, if required */
 
-char *get_afmt_string(int afmt)
+static char *get_afmt_string(int afmt)
 {
         switch(afmt) {
             case AFMT_MU_LAW:
@@ -1551,4 +1547,3 @@
 EXPORT_SYMBOL(dmasound_ulaw2dma8);
 EXPORT_SYMBOL(dmasound_alaw2dma8);
 #endif
-EXPORT_SYMBOL(get_afmt_string) ;
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 202e8103..06e9e88 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -710,7 +710,7 @@
 /*** Config & Setup **********************************************************/
 
 
-int __init dmasound_paula_init(void)
+static int __init dmasound_paula_init(void)
 {
 	int err;
 
diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c
index b3379dd..1855b14 100644
--- a/sound/oss/dmasound/dmasound_q40.c
+++ b/sound/oss/dmasound/dmasound_q40.c
@@ -611,7 +611,7 @@
 /*** Config & Setup **********************************************************/
 
 
-int __init dmasound_q40_init(void)
+static int __init dmasound_q40_init(void)
 {
 	if (MACH_IS_Q40) {
 	    dmasound.mach = machQ40;
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
index ba38d62..e4282d9 100644
--- a/sound/oss/msnd.c
+++ b/sound/oss/msnd.c
@@ -20,8 +20,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.c,v 1.17 1999/03/21 16:50:09 andrewtv Exp $
- *
  ********************************************************************/
 
 #include <linux/module.h>
diff --git a/sound/oss/msnd.h b/sound/oss/msnd.h
index d0ca582..61b3955 100644
--- a/sound/oss/msnd.h
+++ b/sound/oss/msnd.h
@@ -24,8 +24,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.h,v 1.36 1999/03/21 17:05:42 andrewtv Exp $
- *
  ********************************************************************/
 #ifndef __MSND_H
 #define __MSND_H
diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h
index 7ffea52..1a17dde 100644
--- a/sound/oss/msnd_classic.h
+++ b/sound/oss/msnd_classic.h
@@ -24,8 +24,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  * 
- * $Id: msnd_classic.h,v 1.10 1999/03/21 17:36:09 andrewtv Exp $
- *
  ********************************************************************/
 #ifndef __MSND_CLASSIC_H
 #define __MSND_CLASSIC_H
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index f1f49eb..bf27e00 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -29,13 +29,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.c,v 1.8 2000/12/30 00:33:21 sycamore Exp $
- *
  * 12-3-2000  Modified IO port validation  Steve Sycamore
  *
- *
- * $$$: msnd_pinnacle.c,v 1.75 1999/03/21 16:50:09 andrewtv $$$ $
- *
  ********************************************************************/
 
 #include <linux/kernel.h>
diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h
index cce9114..c18d66c 100644
--- a/sound/oss/msnd_pinnacle.h
+++ b/sound/oss/msnd_pinnacle.h
@@ -24,8 +24,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.h,v 1.11 1999/03/21 17:36:09 andrewtv Exp $
- *
  ********************************************************************/
 #ifndef __MSND_PINNACLE_H
 #define __MSND_PINNACLE_H
diff --git a/sound/parisc/Kconfig b/sound/parisc/Kconfig
index a5a7f9d..9b61d95 100644
--- a/sound/parisc/Kconfig
+++ b/sound/parisc/Kconfig
@@ -1,15 +1,20 @@
 # ALSA PA-RISC drivers
 
-menu "GSC devices"
-	depends on SND!=n && GSC
+menuconfig SND_GSC
+	bool "GSC sound devices"
+	depends on GSC
+	default y
+	help
+	  Support for GSC sound devices on PA-RISC architectures.
+
+if SND_GSC
 
 config SND_HARMONY
 	tristate "Harmony/Vivace sound chip"
-	depends on SND
 	select SND_PCM
 	help
 	  Say 'Y' or 'M' to include support for the Harmony/Vivace sound
 	  chip found in most GSC-based PA-RISC workstations.  It's frequently
 	  provided as part of the Lasi multi-function IC.
 
-endmenu
+endif	# SND_GSC
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 7e47421..8fe5dac 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -1,11 +1,16 @@
 # ALSA PCI drivers
 
-menu "PCI devices"
-	depends on SND!=n && PCI
+menuconfig SND_PCI
+	bool "PCI sound devices"
+	depends on PCI
+	default y
+	help
+	  Support for sound devices connected via the PCI bus.
+
+if SND_PCI
 
 config SND_AD1889
 	tristate "Analog Devices AD1889"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated AC97 sound
@@ -17,7 +22,6 @@
 
 config SND_ALS300
 	tristate "Avance Logic ALS300/ALS300+"
-	depends on SND
 	select SND_PCM
 	select SND_AC97_CODEC
 	select SND_OPL3_LIB
@@ -29,7 +33,7 @@
 
 config SND_ALS4000
 	tristate "Avance Logic ALS4000"
-	depends on SND && ISA_DMA_API
+	depends on ISA_DMA_API
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -43,7 +47,6 @@
 
 config SND_ALI5451
 	tristate "ALi M5451 PCI Audio Controller"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -57,7 +60,6 @@
 
 config SND_ATIIXP
 	tristate "ATI IXP AC97 Controller"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated AC97 sound
@@ -69,7 +71,6 @@
 
 config SND_ATIIXP_MODEM
 	tristate "ATI IXP Modem"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated MC97 modem on
@@ -80,7 +81,6 @@
 
 config SND_AU8810
 	tristate "Aureal Advantage"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -95,7 +95,6 @@
 
 config SND_AU8820
 	tristate "Aureal Vortex"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -109,7 +108,6 @@
 
 config SND_AU8830
 	tristate "Aureal Vortex 2"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -124,7 +122,6 @@
 
 config SND_AW2
 	tristate "Emagic Audiowerk 2"
-	depends on SND
 	help
 	  Say Y here to include support for Emagic Audiowerk 2 soundcards.
 
@@ -139,7 +136,7 @@
 
 config SND_AZT3328
 	tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
-	depends on SND && EXPERIMENTAL
+	depends on EXPERIMENTAL
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -152,7 +149,6 @@
 
 config SND_BT87X
 	tristate "Bt87x Audio Capture"
-	depends on SND
 	select SND_PCM
 	help
 	  If you want to record audio from TV cards based on
@@ -174,7 +170,6 @@
 
 config SND_CA0106
 	tristate "SB Audigy LS / Live 24bit"
-	depends on SND
 	select SND_AC97_CODEC
 	select SND_RAWMIDI
 	select SND_VMASTER
@@ -187,7 +182,6 @@
 
 config SND_CMIPCI
 	tristate "C-Media 8338, 8738, 8768, 8770"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_PCM
@@ -201,13 +195,11 @@
 
 config SND_OXYGEN_LIB
         tristate
-	depends on SND
 	select SND_PCM
 	select SND_MPU401_UART
 
 config SND_OXYGEN
 	tristate "C-Media 8788 (Oxygen)"
-	depends on SND
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for sound cards based on the
@@ -225,7 +217,6 @@
 
 config SND_CS4281
 	tristate "Cirrus Logic (Sound Fusion) CS4281"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
@@ -237,7 +228,6 @@
 
 config SND_CS46XX
 	tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
 	help
@@ -258,7 +248,7 @@
 
 config SND_CS5530
 	tristate "CS5530 Audio"
-	depends on SND && ISA_DMA_API
+	depends on ISA_DMA_API
 	select SND_SB16_DSP
 	help
 	  Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
@@ -268,7 +258,7 @@
 
 config SND_CS5535AUDIO
 	tristate "CS5535/CS5536 Audio"
-	depends on SND && X86 && !X86_64
+	depends on X86 && !X86_64
 	select SND_PCM
 	select SND_AC97_CODEC
 	help
@@ -286,7 +276,6 @@
 
 config SND_DARLA20
 	tristate "(Echoaudio) Darla20"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -297,7 +286,6 @@
 
 config SND_GINA20
 	tristate "(Echoaudio) Gina20"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -308,7 +296,6 @@
 
 config SND_LAYLA20
 	tristate "(Echoaudio) Layla20"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -320,7 +307,6 @@
 
 config SND_DARLA24
 	tristate "(Echoaudio) Darla24"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -331,7 +317,6 @@
 
 config SND_GINA24
 	tristate "(Echoaudio) Gina24"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -342,7 +327,6 @@
 
 config SND_LAYLA24
 	tristate "(Echoaudio) Layla24"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -354,7 +338,6 @@
 
 config SND_MONA
 	tristate "(Echoaudio) Mona"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -366,7 +349,6 @@
 
 config SND_MIA
 	tristate "(Echoaudio) Mia"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -378,7 +360,6 @@
 
 config SND_ECHO3G
 	tristate "(Echoaudio) 3G cards"
-	depends on SND
 	select FW_LOADER
 	select SND_RAWMIDI
 	select SND_PCM
@@ -390,7 +371,6 @@
 
 config SND_INDIGO
 	tristate "(Echoaudio) Indigo"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -401,7 +381,6 @@
 
 config SND_INDIGOIO
 	tristate "(Echoaudio) Indigo IO"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -412,7 +391,6 @@
 
 config SND_INDIGODJ
 	tristate "(Echoaudio) Indigo DJ"
-	depends on SND
 	select FW_LOADER
 	select SND_PCM
 	help
@@ -423,7 +401,6 @@
 
 config SND_EMU10K1
 	tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
-	depends on SND
 	select FW_LOADER
 	select SND_HWDEP
 	select SND_RAWMIDI
@@ -441,7 +418,6 @@
 
 config SND_EMU10K1X
 	tristate "Emu10k1X (Dell OEM Version)"
-	depends on SND
 	select SND_AC97_CODEC
 	select SND_RAWMIDI
 	help
@@ -453,7 +429,6 @@
 
 config SND_ENS1370
 	tristate "(Creative) Ensoniq AudioPCI 1370"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_PCM
 	help
@@ -464,7 +439,6 @@
 
 config SND_ENS1371
 	tristate "(Creative) Ensoniq AudioPCI 1371/1373"
-	depends on SND
 	select SND_RAWMIDI
 	select SND_AC97_CODEC
 	help
@@ -476,7 +450,6 @@
 
 config SND_ES1938
 	tristate "ESS ES1938/1946/1969 (Solo-1)"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
@@ -489,7 +462,6 @@
 
 config SND_ES1968
 	tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -501,7 +473,6 @@
 
 config SND_FM801
 	tristate "ForteMedia FM801"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
@@ -528,7 +499,6 @@
 
 config SND_HDA_INTEL
 	tristate "Intel HD Audio"
-	depends on SND
 	select SND_PCM
 	select SND_VMASTER
 	help
@@ -637,7 +607,6 @@
 
 config SND_HDSP
 	tristate "RME Hammerfall DSP Audio"
-	depends on SND
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -650,7 +619,6 @@
 
 config SND_HDSPM
 	tristate "RME Hammerfall DSP MADI"
-	depends on SND
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -663,7 +631,6 @@
 
 config SND_HIFIER
 	tristate "TempoTec HiFier Fantasia"
-	depends on SND
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for the MediaTek/TempoTec HiFier
@@ -674,7 +641,6 @@
 
 config SND_ICE1712
 	tristate "ICEnsemble ICE1712 (Envy24)"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -691,8 +657,7 @@
 
 config SND_ICE1724
 	tristate "ICE/VT1724/1720 (Envy24HT/PT)"
-	depends on SND
-	select SND_MPU401_UART
+	select SND_RAWMIDI
 	select SND_AC97_CODEC
 	select SND_VMASTER
 	help
@@ -709,7 +674,6 @@
 
 config SND_INTEL8X0
 	tristate "Intel/SiS/nVidia/AMD/ALi AC97 Controller"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated AC97 sound
@@ -722,7 +686,6 @@
 
 config SND_INTEL8X0M
 	tristate "Intel/SiS/nVidia/AMD MC97 Modem"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated MC97 modem on
@@ -733,7 +696,6 @@
 
 config SND_KORG1212
 	tristate "Korg 1212 IO"
-	depends on SND
 	select FW_LOADER if !SND_KORG1212_FIRMWARE_IN_KERNEL
 	select SND_PCM
 	help
@@ -753,7 +715,6 @@
 
 config SND_MAESTRO3
 	tristate "ESS Allegro/Maestro3"
-	depends on SND
 	select FW_LOADER if !SND_MAESTRO3_FIRMWARE_IN_KERNEL
 	select SND_AC97_CODEC
 	help
@@ -774,7 +735,6 @@
 
 config SND_MIXART
 	tristate "Digigram miXart"
-	depends on SND
 	select SND_HWDEP
 	select SND_PCM
 	help
@@ -786,7 +746,6 @@
 
 config SND_NM256
 	tristate "NeoMagic NM256AV/ZX"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for NeoMagic NM256AV/ZX chips.
@@ -796,7 +755,6 @@
 
 config SND_PCXHR
 	tristate "Digigram PCXHR"
-	depends on SND
 	select SND_PCM
 	select SND_HWDEP
 	help
@@ -807,7 +765,6 @@
 
 config SND_RIPTIDE
 	tristate "Conexant Riptide"
-	depends on SND
 	select FW_LOADER
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -820,7 +777,6 @@
 
 config SND_RME32
 	tristate "RME Digi32, 32/8, 32 PRO"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y to include support for RME Digi32, Digi32 PRO and
@@ -832,7 +788,6 @@
 
 config SND_RME96
 	tristate "RME Digi96, 96/8, 96/8 PRO"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include support for RME Digi96, Digi96/8 and
@@ -843,7 +798,6 @@
 
 config SND_RME9652
 	tristate "RME Digi9652 (Hammerfall)"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include support for RME Hammerfall (RME
@@ -854,7 +808,7 @@
 
 config SND_SIS7019
 	tristate "SiS 7019 Audio Accelerator"
-	depends on SND && X86 && !X86_64
+	depends on X86 && !X86_64
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the SiS 7019 Audio Accelerator.
@@ -864,7 +818,6 @@
 
 config SND_SONICVIBES
 	tristate "S3 SonicVibes"
-	depends on SND
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
@@ -877,7 +830,6 @@
 
 config SND_TRIDENT
 	tristate "Trident 4D-Wave DX/NX; SiS 7018"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -889,7 +841,6 @@
 
 config SND_VIA82XX
 	tristate "VIA 82C686A/B, 8233/8235 AC97 Controller"
-	depends on SND
 	select SND_MPU401_UART
 	select SND_AC97_CODEC
 	help
@@ -901,7 +852,6 @@
 
 config SND_VIA82XX_MODEM
 	tristate "VIA 82C686A/B, 8233 based Modems"
-	depends on SND
 	select SND_AC97_CODEC
 	help
 	  Say Y here to include support for the integrated MC97 modem on
@@ -912,7 +862,6 @@
 
 config SND_VIRTUOSO
 	tristate "Asus Virtuoso 100/200 (Xonar)"
-	depends on SND
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for sound cards based on the
@@ -923,7 +872,6 @@
 
 config SND_VX222
 	tristate "Digigram VX222"
-	depends on SND
 	select SND_VX_LIB
 	help
 	  Say Y here to include support for Digigram VX222 soundcards.
@@ -933,7 +881,6 @@
 
 config SND_YMFPCI
 	tristate "Yamaha YMF724/740/744/754"
-	depends on SND
 	select FW_LOADER if !SND_YMFPCI_FIRMWARE_IN_KERNEL
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
@@ -954,25 +901,4 @@
 	  for the YMFPCI driver.  If you choose N here, you need to
 	  install the firmware files from the alsa-firmware package.
 
-config SND_AC97_POWER_SAVE
-	bool "AC97 Power-Saving Mode"
-	depends on SND_AC97_CODEC && EXPERIMENTAL
-	default n
-	help
-	  Say Y here to enable the aggressive power-saving support of
-	  AC97 codecs.  In this mode, the power-mode is dynamically
-	  controlled at each open/close.
-
-	  The mode is activated by passing power_save=1 option to
-	  snd-ac97-codec driver.  You can toggle it dynamically over
-	  sysfs, too.
-
-config SND_AC97_POWER_SAVE_DEFAULT
-	int "Default time-out for AC97 power-save mode"
-	depends on SND_AC97_POWER_SAVE
-	default 0
-	help
-	  The default time-out value in seconds for AC97 automatic
-	  power-save mode.  0 means to disable the power-save mode.
-
-endmenu
+endif	# SND_PCI
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 85ef14b..65b25d2 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -13,7 +13,7 @@
 snd-cmipci-objs := cmipci.o
 snd-cs4281-objs := cs4281.o
 snd-cs5530-objs := cs5530.o
-snd-ens1370-objs := ens1370.o
+snd-ens1370-objs := ens1370.o ak4531_codec.o
 snd-ens1371-objs := ens1371.o
 snd-es1938-objs := es1938.o
 snd-es1968-objs := es1968.o
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index 0be48b1..41fa322 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/Makefile
@@ -3,16 +3,8 @@
 # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
 #
 
-snd-ac97-codec-objs := ac97_codec.o ac97_pcm.o
-
-ifneq ($(CONFIG_PROC_FS),)
-snd-ac97-codec-objs += ac97_proc.o
-endif
-
-snd-ak4531-codec-objs := ak4531_codec.o
+snd-ac97-codec-y := ac97_codec.o ac97_pcm.o
+snd-ac97-codec-$(CONFIG_PROC_FS) += ac97_proc.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o
-obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o
-
-obj-m := $(sort $(obj-m))
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 45fd290..07364c0 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -49,8 +49,9 @@
 
 #ifdef CONFIG_SND_AC97_POWER_SAVE
 static int power_save = CONFIG_SND_AC97_POWER_SAVE_DEFAULT;
-module_param(power_save, bool, 0644);
-MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+		 "(in second, 0 = disable).");
 #endif
 /*
 
@@ -2294,9 +2295,11 @@
 	power |= AC97_PD_PR0 | AC97_PD_PR1;	/* ADC & DAC powerdown */
 	snd_ac97_write(ac97, AC97_POWERDOWN, power);
 	udelay(100);
-	power |= AC97_PD_PR2 | AC97_PD_PR3;	/* Analog Mixer powerdown */
+	power |= AC97_PD_PR2;	/* Analog Mixer powerdown (Vref on) */
 	snd_ac97_write(ac97, AC97_POWERDOWN, power);
 	if (ac97_is_power_save_mode(ac97)) {
+		power |= AC97_PD_PR3;	/* Analog Mixer powerdown */
+		snd_ac97_write(ac97, AC97_POWERDOWN, power);
 		udelay(100);
 		/* AC-link powerdown, internal Clk disable */
 		/* FIXME: this may cause click noises on some boards */
@@ -2362,7 +2365,7 @@
 		 *  that open/close frequently)
 		 */
 		schedule_delayed_work(&ac97->power_work,
-				      msecs_to_jiffies(2000));
+				      msecs_to_jiffies(power_save * 1000));
 	else {
 		cancel_delayed_work(&ac97->power_work);
 		update_power_regs(ac97);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 1292dce..0746e9c 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -669,6 +669,7 @@
 AC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
 AC97_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
 
+AC97_SINGLE("Master Left Inv Switch", AC97_MASTER, 6, 1, 0),
 AC97_SINGLE("Master ZC Switch", AC97_MASTER, 7, 1, 0),
 AC97_SINGLE("Headphone ZC Switch", AC97_HEADPHONE, 7, 1, 0),
 AC97_SINGLE("Mono ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
@@ -3352,8 +3353,66 @@
 AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
 };
 
+static const char *slave_vols_vt1616[] = {
+	"Front Playback Volume",
+	"Surround Playback Volume",
+	"Center Playback Volume",
+	"LFE Playback Volume",
+	NULL
+};
+
+static const char *slave_sws_vt1616[] = {
+	"Front Playback Switch",
+	"Surround Playback Switch",
+	"Center Playback Switch",
+	"LFE Playback Switch",
+	NULL
+};
+
+/* find a mixer control element with the given name */
+static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
+						    const char *name)
+{
+	struct snd_ctl_elem_id id;
+	memset(&id, 0, sizeof(id));
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(id.name, name);
+	return snd_ctl_find_id(ac97->bus->card, &id);
+}
+
+/* create a virtual master control and add slaves */
+int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
+			 const unsigned int *tlv, const char **slaves)
+{
+	struct snd_kcontrol *kctl;
+	const char **s;
+	int err;
+
+	kctl = snd_ctl_make_virtual_master(name, tlv);
+	if (!kctl)
+		return -ENOMEM;
+	err = snd_ctl_add(ac97->bus->card, kctl);
+	if (err < 0)
+		return err;
+
+	for (s = slaves; *s; s++) {
+		struct snd_kcontrol *sctl;
+
+		sctl = snd_ac97_find_mixer_ctl(ac97, *s);
+		if (!sctl) {
+			snd_printdd("Cannot find slave %s, skipped\n", *s);
+			continue;
+		}
+		err = snd_ctl_add_slave(kctl, sctl);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
 static int patch_vt1616_specific(struct snd_ac97 * ac97)
 {
+	struct snd_kcontrol *kctl;
 	int err;
 
 	if (snd_ac97_try_bit(ac97, 0x5a, 9))
@@ -3361,6 +3420,24 @@
 			return err;
 	if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
 		return err;
+
+	/* There is already a misnamed master switch.  Rename it.  */
+	kctl = snd_ac97_find_mixer_ctl(ac97, "Master Playback Volume");
+	if (!kctl)
+		return -EINVAL;
+
+	snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Front Playback");
+
+	err = snd_ac97_add_vmaster(ac97, "Master Playback Volume",
+				   kctl->tlv.p, slave_vols_vt1616);
+	if (err < 0)
+		return err;
+
+	err = snd_ac97_add_vmaster(ac97, "Master Playback Switch",
+				   NULL, slave_sws_vt1616);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -3633,7 +3710,7 @@
 {
 	ac97->build_ops = &patch_ucb1400_ops;
 	/* enable headphone driver and smart low power mode by default */
-	snd_ac97_write(ac97, 0x6a, 0x0050);
-	snd_ac97_write(ac97, 0x6c, 0x0030);
+	snd_ac97_write_cache(ac97, 0x6a, 0x0050);
+	snd_ac97_write_cache(ac97, 0x6c, 0x0030);
 	return 0;
 }
diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ak4531_codec.c
similarity index 96%
rename from sound/pci/ac97/ak4531_codec.c
rename to sound/pci/ak4531_codec.c
index c0c16339..33d37b1 100644
--- a/sound/pci/ac97/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -28,9 +28,11 @@
 #include <sound/ak4531_codec.h>
 #include <sound/tlv.h>
 
+/*
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("Universal routines for AK4531 codec");
 MODULE_LICENSE("GPL");
+*/
 
 #ifdef CONFIG_PROC_FS
 static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531);
@@ -270,7 +272,7 @@
 static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
 
-static struct snd_kcontrol_new snd_ak4531_controls[] = {
+static struct snd_kcontrol_new snd_ak4531_controls[] __devinitdata = {
 
 AK4531_DOUBLE_TLV("Master Playback Switch", 0,
 		  AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
@@ -379,8 +381,9 @@
 	0x01		/* 19: Mic Amp Setup */
 };
 
-int snd_ak4531_mixer(struct snd_card *card, struct snd_ak4531 *_ak4531,
-		     struct snd_ak4531 **rak4531)
+int __devinit snd_ak4531_mixer(struct snd_card *card,
+			       struct snd_ak4531 *_ak4531,
+			       struct snd_ak4531 **rak4531)
 {
 	unsigned int idx;
 	int err;
@@ -476,7 +479,8 @@
 		    ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
 }
 
-static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
+static void __devinit
+snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
 {
 	struct snd_info_entry *entry;
 
@@ -484,25 +488,3 @@
 		snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read);
 }
 #endif
-
-EXPORT_SYMBOL(snd_ak4531_mixer);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_ak4531_suspend);
-EXPORT_SYMBOL(snd_ak4531_resume);
-#endif
-
-/*
- *  INIT part
- */
-
-static int __init alsa_ak4531_init(void)
-{
-	return 0;
-}
-
-static void __exit alsa_ak4531_exit(void)
-{
-}
-
-module_init(alsa_ak4531_init)
-module_exit(alsa_ak4531_exit)
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index bc212f4..e291aa5 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -1,6 +1,4 @@
 /*
- * $Id: au88x0_game.c,v 1.9 2003/09/22 03:51:28 mjander Exp $
- *
  *  Manuel Jander.
  *
  *  Based on the work of:
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 5f63af6..22f18f3 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1,6 +1,6 @@
 /*
  *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- *  Copyright (C) 2002, 2005, 2006, 2007 by Andreas Mohr <andi AT lisas.de>
+ *  Copyright (C) 2002, 2005 - 2008 by Andreas Mohr <andi AT lisas.de>
  *
  *  Framework borrowed from Bart Hartgers's als4000.c.
  *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@@ -35,9 +35,20 @@
  *  (3 weeks' worth of evenings filled with driver work).
  *  (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros)
  *
- *  The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
- *  for compatibility reasons) has the following features:
+ *  It is quite likely that the AZF3328 chip is the PCI cousin of the
+ *  AZF3318 ("azt1020 pnp", "MM Pro 16") ISA chip, given very similar specs.
  *
+ *  The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name
+ *  for compatibility reasons) from Azfin (joint-venture of Aztech and Fincitec,
+ *  Fincitec acquired by National Semiconductor in 2002, together with the
+ *  Fincitec-related company ARSmikro) has the following features:
+ *
+ *  - compatibility & compliance:
+ *    - Microsoft PC 97 ("PC 97 Hardware Design Guide",
+ *                       http://www.microsoft.com/whdc/archive/pcguides.mspx)
+ *    - Microsoft PC 98 Baseline Audio
+ *    - MPU401 UART
+ *    - Sound Blaster Emulation (DOS Box)
  *  - builtin AC97 conformant codec (SNR over 80dB)
  *    Note that "conformant" != "compliant"!! this chip's mixer register layout
  *    *differs* from the standard AC97 layout:
@@ -48,21 +59,28 @@
  *    addresses illegally. So far unfortunately it looks like the very flexible
  *    ALSA AC97 support is still not enough to easily compensate for such a
  *    grave layout violation despite all tweaks and quirks mechanisms it offers.
- *  - builtin genuine OPL3
+ *  - builtin genuine OPL3 - verified to work fine, 20080506
  *  - full duplex 16bit playback/record at independent sampling rate
- *  - MPU401 (+ legacy address support) FIXME: how to enable legacy addr??
+ *  - MPU401 (+ legacy address support, claimed by one official spec sheet)
+ *    FIXME: how to enable legacy addr??
  *  - game port (legacy address support)
- *  - builtin 3D enhancement (said to be YAMAHA Ymersion)
  *  - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven
- *    features supported)
+ *    features supported). - See common term "Digital Enhanced Game Port"...
+ *    (probably DirectInput 3.0 spec - confirm)
+ *  - builtin 3D enhancement (said to be YAMAHA Ymersion)
  *  - built-in General DirectX timer having a 20 bits counter
  *    with 1us resolution (see below!)
- *  - I2S serial port for external DAC
+ *  - I2S serial output port for external DAC
  *  - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
  *  - supports hardware volume control
  *  - single chip low cost solution (128 pin QFP)
  *  - supports programmable Sub-vendor and Sub-system ID
  *    required for Microsoft's logo compliance (FIXME: where?)
+ *    At least the Trident 4D Wave DX has one bit somewhere
+ *    to enable writes to PCI subsystem VID registers, that should be it.
+ *    This might easily be in extended PCI reg space, since PCI168 also has
+ *    some custom data starting at 0x80. What kind of config settings
+ *    are located in our extended PCI space anyway??
  *  - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
  *
  *  Note that this driver now is actually *better* than the Windows driver,
@@ -74,6 +92,24 @@
  *  - "timidity -iAv -B2,8 -Os -EFreverb=0"
  *  - "pmidi -p 128:0 jazz.mid"
  *
+ *  OPL3 hardware playback testing, try something like:
+ *  cat /proc/asound/hwdep
+ *  and
+ *  aconnect -o
+ *  Then use
+ *  sbiload -Dhw:x,y --opl3 /usr/share/sounds/opl3/std.o3 ......./drums.o3
+ *  where x,y is the xx-yy number as given in hwdep.
+ *  Then try
+ *  pmidi -p a:b jazz.mid
+ *  where a:b is the client number plus 0 usually, as given by aconnect above.
+ *  Oh, and make sure to unmute the FM mixer control (doh!)
+ *  NOTE: power use during OPL3 playback is _VERY_ high (70W --> 90W!)
+ *  despite no CPU activity, possibly due to hindering ACPI idling somehow.
+ *  Shouldn't be a problem of the AZF3328 chip itself, I'd hope.
+ *  Higher PCM / FM mixer levels seem to conflict (causes crackling),
+ *  at least sometimes.   Maybe even use with hardware sequencer timer above :)
+ *  adplay/adplug-utils might soon offer hardware-based OPL3 playback, too.
+ *
  *  Certain PCI versions of this card are susceptible to DMA traffic underruns
  *  in some systems (resulting in sound crackling/clicking/popping),
  *  probably because they don't have a DMA FIFO buffer or so.
@@ -87,6 +123,8 @@
  *  better than a VIA, yet ironically I still get crackling, like many other
  *  people with the same chipset.
  *  Possible remedies:
+ *  - use speaker (amplifier) output instead of headphone output
+ *    (in case crackling is due to overloaded output clipping)
  *  - plug card into a different PCI slot, preferrably one that isn't shared
  *    too much (this helps a lot, but not completely!)
  *  - get rid of PCI VGA card, use AGP instead
@@ -94,18 +132,23 @@
  *  - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX)
  *    Not too helpful.
  *  - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS
- * 
+ *
  * BUGS
- *  - full-duplex might *still* be problematic, not fully tested recently
+ *  - full-duplex might *still* be problematic, however a recent test was fine
  *  - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated
  *    if you set PCM output switch to "pre 3D" instead of "post 3D".
  *    If this can't be set, then get a mixer application that Isn't Stupid (tm)
  *    (e.g. kmix, gamix) - unfortunately several are!!
- * 
+ *  - locking is not entirely clean, especially the audio stream activity
+ *    ints --> may be racy
+ *  - an _unconnected_ secondary joystick at the gameport will be reported
+ *    to be "active" (floating values, not precisely -1) due to the way we need
+ *    to read the Digital Enhanced Game Port. Not sure whether it is fixable.
+ *
  * TODO
  *  - test MPU401 MIDI playback etc.
- *  - add some power micro-management (disable various units of the card
- *    as long as they're unused). However this requires I/O ports which I
+ *  - add more power micro-management (disable various units of the card
+ *    as long as they're unused). However this requires more I/O ports which I
  *    haven't figured out yet and which thus might not even exist...
  *    The standard suspend/resume functionality could probably make use of
  *    some improvement, too...
@@ -113,6 +156,7 @@
  *  - figure out some cleverly evil scheme to possibly make ALSA AC97 code
  *    fully accept our quite incompatible ""AC97"" mixer and thus save some
  *    code (but I'm not too optimistic that doing this is possible at all)
+ *  - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
  */
 
 #include <asm/io.h>
@@ -138,7 +182,7 @@
 MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 
 #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
-#define SUPPORT_JOYSTICK 1
+#define SUPPORT_GAMEPORT 1
 #endif
 
 #define DEBUG_MISC	0
@@ -147,13 +191,14 @@
 #define DEBUG_PLAY_REC	0
 #define DEBUG_IO	0
 #define DEBUG_TIMER	0
+#define DEBUG_GAME	0
 #define MIXER_TESTING	0
 
 #if DEBUG_MISC
 #define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)
 #else
 #define snd_azf3328_dbgmisc(format, args...)
-#endif		
+#endif
 
 #if DEBUG_CALLS
 #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
@@ -163,25 +208,31 @@
 #define snd_azf3328_dbgcalls(format, args...)
 #define snd_azf3328_dbgcallenter()
 #define snd_azf3328_dbgcallleave()
-#endif		
+#endif
 
 #if DEBUG_MIXER
 #define snd_azf3328_dbgmixer(format, args...) printk(format, ##args)
 #else
 #define snd_azf3328_dbgmixer(format, args...)
-#endif		
+#endif
 
 #if DEBUG_PLAY_REC
 #define snd_azf3328_dbgplay(format, args...) printk(KERN_ERR format, ##args)
 #else
 #define snd_azf3328_dbgplay(format, args...)
-#endif		
+#endif
 
 #if DEBUG_MISC
 #define snd_azf3328_dbgtimer(format, args...) printk(KERN_ERR format, ##args)
 #else
 #define snd_azf3328_dbgtimer(format, args...)
-#endif		
+#endif
+
+#if DEBUG_GAME
+#define snd_azf3328_dbggame(format, args...) printk(KERN_ERR format, ##args)
+#else
+#define snd_azf3328_dbggame(format, args...)
+#endif
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 module_param_array(index, int, NULL, 0444);
@@ -195,51 +246,62 @@
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
 
-#ifdef SUPPORT_JOYSTICK
-static int joystick[SNDRV_CARDS];
-module_param_array(joystick, bool, NULL, 0444);
-MODULE_PARM_DESC(joystick, "Enable joystick for AZF3328 soundcard.");
-#endif
-
 static int seqtimer_scaling = 128;
 module_param(seqtimer_scaling, int, 0444);
 MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
 
+struct snd_azf3328_audio_stream {
+	struct snd_pcm_substream *substream;
+	int enabled;
+	int running;
+	unsigned long portbase;
+};
+
+enum snd_azf3328_stream_index {
+  AZF_PLAYBACK = 0,
+  AZF_CAPTURE = 1,
+};
+
 struct snd_azf3328 {
 	/* often-used fields towards beginning, then grouped */
-	unsigned long codec_port;
-	unsigned long io2_port;
-	unsigned long mpu_port;
-	unsigned long synth_port;
-	unsigned long mixer_port;
+
+	unsigned long codec_io; /* usually 0xb000, size 128 */
+	unsigned long game_io;  /* usually 0xb400, size 8 */
+	unsigned long mpu_io;   /* usually 0xb800, size 4 */
+	unsigned long opl3_io; /* usually 0xbc00, size 8 */
+	unsigned long mixer_io; /* usually 0xc000, size 64 */
 
 	spinlock_t reg_lock;
 
 	struct snd_timer *timer;
-	
+
 	struct snd_pcm *pcm;
-	struct snd_pcm_substream *playback_substream;
-	struct snd_pcm_substream *capture_substream;
-	unsigned int is_playing;
-	unsigned int is_recording;
+	struct snd_azf3328_audio_stream audio_stream[2];
 
 	struct snd_card *card;
 	struct snd_rawmidi *rmidi;
 
-#ifdef SUPPORT_JOYSTICK
+#ifdef SUPPORT_GAMEPORT
 	struct gameport *gameport;
+	int axes[4];
 #endif
 
 	struct pci_dev *pci;
 	int irq;
 
+	/* register 0x6a is write-only, thus need to remember setting.
+	 * If we need to add more registers here, then we might try to fold this
+	 * into some transparent combined shadow register handling with
+	 * CONFIG_PM register storage below, but that's slightly difficult. */
+	u16 shadow_reg_codec_6AH;
+
 #ifdef CONFIG_PM
 	/* register value containers for power management
 	 * Note: not always full I/O range preserved (just like Win driver!) */
-	u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2];
-	u16 saved_regs_io2   [AZF_IO_SIZE_IO2_PM / 2];
-	u16 saved_regs_mpu   [AZF_IO_SIZE_MPU_PM / 2];
-	u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2];
+	u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2];
+	u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2];
+	u16 saved_regs_mpu  [AZF_IO_SIZE_MPU_PM / 2];
+	u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2];
 	u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
 #endif
 };
@@ -252,126 +314,166 @@
 
 MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
 
-static inline void
-snd_azf3328_codec_outb(const struct snd_azf3328 *chip, int reg, u8 value)
+
+static int
+snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
 {
-	outb(value, chip->codec_port + reg);
+	u8 prev = inb(reg), new;
+
+	new = (do_set) ? (prev|mask) : (prev & ~mask);
+	/* we need to always write the new value no matter whether it differs
+	 * or not, since some register bits don't indicate their setting */
+	outb(new, reg);
+	if (new != prev)
+		return 1;
+
+	return 0;
+}
+
+static inline void
+snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
+{
+	outb(value, chip->codec_io + reg);
 }
 
 static inline u8
-snd_azf3328_codec_inb(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_codec_inb(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inb(chip->codec_port + reg);
+	return inb(chip->codec_io + reg);
 }
 
 static inline void
-snd_azf3328_codec_outw(const struct snd_azf3328 *chip, int reg, u16 value)
+snd_azf3328_codec_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
 {
-	outw(value, chip->codec_port + reg);
+	outw(value, chip->codec_io + reg);
 }
 
 static inline u16
-snd_azf3328_codec_inw(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_codec_inw(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inw(chip->codec_port + reg);
+	return inw(chip->codec_io + reg);
 }
 
 static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328 *chip, int reg, u32 value)
+snd_azf3328_codec_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
 {
-	outl(value, chip->codec_port + reg);
+	outl(value, chip->codec_io + reg);
+}
+
+static inline u32
+snd_azf3328_codec_inl(const struct snd_azf3328 *chip, unsigned reg)
+{
+	return inl(chip->codec_io + reg);
 }
 
 static inline void
-snd_azf3328_io2_outb(const struct snd_azf3328 *chip, int reg, u8 value)
+snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
 {
-	outb(value, chip->io2_port + reg);
+	outb(value, chip->game_io + reg);
+}
+
+static inline void
+snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
+{
+	outw(value, chip->game_io + reg);
 }
 
 static inline u8
-snd_azf3328_io2_inb(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inb(chip->io2_port + reg);
-}
-
-static inline void
-snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, int reg, u16 value)
-{
-	outw(value, chip->mixer_port + reg);
+	return inb(chip->game_io + reg);
 }
 
 static inline u16
-snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, int reg)
+snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
 {
-	return inw(chip->mixer_port + reg);
+	return inw(chip->game_io + reg);
 }
 
-static void
-snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, int reg, int do_mute)
+static inline void
+snd_azf3328_mixer_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
 {
-	unsigned long portbase = chip->mixer_port + reg + 1;
-	unsigned char oldval;
+	outw(value, chip->mixer_io + reg);
+}
+
+static inline u16
+snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
+{
+	return inw(chip->mixer_io + reg);
+}
+
+#define AZF_MUTE_BIT 0x80
+
+static int
+snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
+			   unsigned reg, int do_mute
+)
+{
+	unsigned long portbase = chip->mixer_io + reg + 1;
+	int updated;
 
 	/* the mute bit is on the *second* (i.e. right) register of a
 	 * left/right channel setting */
-	oldval = inb(portbase);
-	if (do_mute)
-		oldval |= 0x80;
-	else
-		oldval &= ~0x80;
-	outb(oldval, portbase);
+	updated = snd_azf3328_io_reg_setb(portbase, AZF_MUTE_BIT, do_mute);
+
+	/* indicate whether it was muted before */
+	return (do_mute) ? !updated : updated;
 }
 
 static void
-snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg, unsigned char dst_vol_left, unsigned char dst_vol_right, int chan_sel, int delay)
+snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
+					 unsigned reg,
+					 unsigned char dst_vol_left,
+					 unsigned char dst_vol_right,
+					 int chan_sel, int delay
+)
 {
-	unsigned long portbase = chip->mixer_port + reg;
+	unsigned long portbase = chip->mixer_io + reg;
 	unsigned char curr_vol_left = 0, curr_vol_right = 0;
-	int left_done = 0, right_done = 0;
-	
+	int left_change = 0, right_change = 0;
+
 	snd_azf3328_dbgcallenter();
-	if (chan_sel & SET_CHAN_LEFT)
+
+	if (chan_sel & SET_CHAN_LEFT) {
 		curr_vol_left  = inb(portbase + 1);
-	else
-		left_done = 1;
-	if (chan_sel & SET_CHAN_RIGHT)
+
+		/* take care of muting flag contained in left channel */
+		if (curr_vol_left & AZF_MUTE_BIT)
+			dst_vol_left |= AZF_MUTE_BIT;
+		else
+			dst_vol_left &= ~AZF_MUTE_BIT;
+
+		left_change = (curr_vol_left > dst_vol_left) ? -1 : 1;
+	}
+
+	if (chan_sel & SET_CHAN_RIGHT) {
 		curr_vol_right = inb(portbase + 0);
-	else
-		right_done = 1;
-	
-	/* take care of muting flag (0x80) contained in left channel */
-	if (curr_vol_left & 0x80)
-		dst_vol_left |= 0x80;
-	else
-		dst_vol_left &= ~0x80;
+
+		right_change = (curr_vol_right > dst_vol_right) ? -1 : 1;
+	}
 
 	do {
-		if (!left_done) {
-			if (curr_vol_left > dst_vol_left)
-				curr_vol_left--;
-			else
-			if (curr_vol_left < dst_vol_left)
-				curr_vol_left++;
-			else
-			    left_done = 1;
-			outb(curr_vol_left, portbase + 1);
+		if (left_change) {
+			if (curr_vol_left != dst_vol_left) {
+				curr_vol_left += left_change;
+				outb(curr_vol_left, portbase + 1);
+			} else
+			    left_change = 0;
 		}
-		if (!right_done) {
-			if (curr_vol_right > dst_vol_right)
-				curr_vol_right--;
-			else
-			if (curr_vol_right < dst_vol_right)
-				curr_vol_right++;
-			else
-			    right_done = 1;
+		if (right_change) {
+			if (curr_vol_right != dst_vol_right) {
+				curr_vol_right += right_change;
+
 			/* during volume change, the right channel is crackling
 			 * somewhat more than the left channel, unfortunately.
 			 * This seems to be a hardware issue. */
-			outb(curr_vol_right, portbase + 0);
+				outb(curr_vol_right, portbase + 0);
+			} else
+			    right_change = 0;
 		}
 		if (delay)
 			mdelay(delay);
-	} while ((!left_done) || (!right_done));
+	} while ((left_change) || (right_change));
 	snd_azf3328_dbgcallleave();
 }
 
@@ -379,7 +481,7 @@
  * general mixer element
  */
 struct azf3328_mixer_reg {
-	unsigned int reg;
+	unsigned reg;
 	unsigned int lchan_shift, rchan_shift;
 	unsigned int mask;
 	unsigned int invert: 1;
@@ -544,13 +646,14 @@
 		"Mix", "Mic"
 	};
 	static const char * const texts3[] = {
-                "Mic", "CD", "Video", "Aux",
+		"Mic", "CD", "Video", "Aux",
 		"Line", "Mix", "Mix Mono", "Phone"
         };
 	static const char * const texts4[] = {
 		"pre 3D", "post 3D"
         };
 	struct azf3328_mixer_reg reg;
+	const char * const *p = NULL;
 
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
         uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -561,18 +664,20 @@
 	if (reg.reg == IDX_MIXER_ADVCTL2) {
 		switch(reg.lchan_shift) {
 		case 8: /* modem out sel */
-			strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]);
+			p = texts1;
 			break;
 		case 9: /* mono sel source */
-			strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]);
+			p = texts2;
 			break;
 		case 15: /* PCM Out Path */
-			strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]);
+			p = texts4;
 			break;
 		}
 	} else
-        	strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item]
-);
+	if (reg.reg == IDX_MIXER_REC_SELECT)
+		p = texts3;
+
+	strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
         return 0;
 }
 
@@ -583,7 +688,7 @@
         struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct azf3328_mixer_reg reg;
         unsigned short val;
-        
+
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	val = snd_azf3328_mixer_inw(chip, reg.reg);
 	if (reg.reg == IDX_MIXER_REC_SELECT) {
@@ -605,7 +710,7 @@
         struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct azf3328_mixer_reg reg;
 	unsigned int oreg, nreg, val;
-        
+
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	oreg = snd_azf3328_mixer_inw(chip, reg.reg);
 	val = oreg;
@@ -631,9 +736,11 @@
 static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
 	AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
 	AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
-	AZF3328_MIXER_SWITCH("Wave Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
-	AZF3328_MIXER_VOL_STEREO("Wave Playback Volume", IDX_MIXER_WAVEOUT, 0x1f, 1),
-	AZF3328_MIXER_SWITCH("Wave 3D Bypass Playback Switch", IDX_MIXER_ADVCTL2, 7, 1),
+	AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
+	AZF3328_MIXER_VOL_STEREO("PCM Playback Volume",
+					IDX_MIXER_WAVEOUT, 0x1f, 1),
+	AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch",
+					IDX_MIXER_ADVCTL2, 7, 1),
 	AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1),
 	AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1),
 	AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1),
@@ -717,15 +824,16 @@
 	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
 
 	/* mute and zero volume channels */
-	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); idx++) {
+	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_init_values); ++idx) {
 		snd_azf3328_mixer_outw(chip,
 			snd_azf3328_init_values[idx][0],
 			snd_azf3328_init_values[idx][1]);
 	}
-	
+
 	/* add mixer controls */
 	sw = snd_azf3328_mixer_controls;
-	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls); idx++, sw++) {
+	for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
+			++idx, ++sw) {
 		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
 			return err;
 	}
@@ -757,9 +865,9 @@
 }
 
 static void
-snd_azf3328_setfmt(struct snd_azf3328 *chip,
-			       unsigned int reg,
-			       unsigned int bitrate,
+snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
+			       unsigned reg,
+			       enum azf_freq_t bitrate,
 			       unsigned int format_width,
 			       unsigned int channels
 )
@@ -769,24 +877,25 @@
 
 	snd_azf3328_dbgcallenter();
 	switch (bitrate) {
-	case  4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
-	case  4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
-	case  5512: val |= SOUNDFORMAT_FREQ_5510; break; /* the AZF3328 names it "5510" for some strange reason */
-	case  6620: val |= SOUNDFORMAT_FREQ_6620; break;
-	case  8000: val |= SOUNDFORMAT_FREQ_8000; break;
-	case  9600: val |= SOUNDFORMAT_FREQ_9600; break;
-	case 11025: val |= SOUNDFORMAT_FREQ_11025; break;
-	case 13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
-	case 16000: val |= SOUNDFORMAT_FREQ_16000; break;
-	case 22050: val |= SOUNDFORMAT_FREQ_22050; break;
-	case 32000: val |= SOUNDFORMAT_FREQ_32000; break;
-	case 44100: val |= SOUNDFORMAT_FREQ_44100; break;
-	case 48000: val |= SOUNDFORMAT_FREQ_48000; break;
-	case 66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
+	case AZF_FREQ_4000:  val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
+	case AZF_FREQ_4800:  val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
+	case AZF_FREQ_5512:
+		/* the AZF3328 names it "5510" for some strange reason */
+			     val |= SOUNDFORMAT_FREQ_5510; break;
+	case AZF_FREQ_6620:  val |= SOUNDFORMAT_FREQ_6620; break;
+	case AZF_FREQ_8000:  val |= SOUNDFORMAT_FREQ_8000; break;
+	case AZF_FREQ_9600:  val |= SOUNDFORMAT_FREQ_9600; break;
+	case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
+	case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
+	case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
+	case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
+	case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
 	default:
 		snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
-		val |= SOUNDFORMAT_FREQ_44100;
-		break;
+		/* fall-through */
+	case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
+	case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
+	case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
 	}
 	/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
 	/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
@@ -805,10 +914,10 @@
 		val |= SOUNDFORMAT_FLAG_16BIT;
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
-	
+
 	/* set bitrate/format */
 	snd_azf3328_codec_outw(chip, reg, val);
-	
+
 	/* changing the bitrate/format settings switches off the
 	 * audio output with an annoying click in case of 8/16bit format change
 	 * (maybe shutting down DAC/ADC?), thus immediately
@@ -830,31 +939,95 @@
 	snd_azf3328_dbgcallleave();
 }
 
+static inline void
+snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
+			    unsigned reg
+)
+{
+	/* choose lowest frequency for low power consumption.
+	 * While this will cause louder noise due to rather coarse frequency,
+	 * it should never matter since output should always
+	 * get disabled properly when idle anyway. */
+	snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1);
+}
+
+static void
+snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip,
+					unsigned bitmask,
+					int enable
+)
+{
+	if (enable)
+		chip->shadow_reg_codec_6AH &= ~bitmask;
+	else
+		chip->shadow_reg_codec_6AH |= bitmask;
+	snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n",
+			bitmask, enable, chip->shadow_reg_codec_6AH);
+	snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH);
+}
+
+static inline void
+snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_dbgplay("codec_enable %d\n", enable);
+	/* no idea what exactly is being done here, but I strongly assume it's
+	 * PM related */
+	snd_azf3328_codec_reg_6AH_update(
+		chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
+	);
+}
+
+static void
+snd_azf3328_codec_activity(struct snd_azf3328 *chip,
+				enum snd_azf3328_stream_index stream_type,
+				int enable
+)
+{
+	int need_change = (chip->audio_stream[stream_type].running != enable);
+
+	snd_azf3328_dbgplay(
+		"codec_activity: type %d, enable %d, need_change %d\n",
+				stream_type, enable, need_change
+	);
+	if (need_change) {
+		enum snd_azf3328_stream_index other =
+			(stream_type == AZF_PLAYBACK) ?
+				AZF_CAPTURE : AZF_PLAYBACK;
+		/* small check to prevent shutting down the other party
+		 * in case it's active */
+		if ((enable) || !(chip->audio_stream[other].running))
+			snd_azf3328_codec_enable(chip, enable);
+
+		/* ...and adjust clock, too
+		 * (reduce noise and power consumption) */
+		if (!enable)
+			snd_azf3328_codec_setfmt_lowpower(
+				chip,
+				chip->audio_stream[stream_type].portbase
+					+ IDX_IO_PLAY_SOUNDFORMAT
+			);
+	}
+	chip->audio_stream[stream_type].running = enable;
+}
+
 static void
 snd_azf3328_setdmaa(struct snd_azf3328 *chip,
 				long unsigned int addr,
                                 unsigned int count,
                                 unsigned int size,
-				int do_recording)
+				enum snd_azf3328_stream_index stream_type
+)
 {
-	unsigned long flags, portbase;
-	unsigned int is_running;
-
 	snd_azf3328_dbgcallenter();
-	if (do_recording) {
-		/* access capture registers, i.e. skip playback reg section */
-		portbase = chip->codec_port + 0x20;
-		is_running = chip->is_recording;
-	} else {
-		/* access the playback register section */
-		portbase = chip->codec_port + 0x00;
-		is_running = chip->is_playing;
-	}
+	if (!chip->audio_stream[stream_type].running) {
+		/* AZF3328 uses a two buffer pointer DMA playback approach */
 
-	/* AZF3328 uses a two buffer pointer DMA playback approach */
-	if (!is_running) {
-		unsigned long addr_area2;
-		unsigned long count_areas, count_tmp; /* width 32bit -- overflow!! */
+		unsigned long flags, portbase, addr_area2;
+
+		/* width 32bit (prevent overflow): */
+		unsigned long count_areas, count_tmp;
+
+		portbase = chip->audio_stream[stream_type].portbase;
 		count_areas = size/2;
 		addr_area2 = addr+count_areas;
 		count_areas--; /* max. index */
@@ -884,11 +1057,11 @@
 
 	snd_azf3328_dbgcallenter();
 #if 0
-	snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+	snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
 		runtime->rate,
 		snd_pcm_format_width(runtime->format),
 		runtime->channels);
-	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 0);
+	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_PLAYBACK);
 #endif
 	snd_azf3328_dbgcallleave();
 	return 0;
@@ -906,11 +1079,11 @@
 
 	snd_azf3328_dbgcallenter();
 #if 0
-	snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
+	snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
 		runtime->rate,
 		snd_pcm_format_width(runtime->format),
 		runtime->channels);
-	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, 1);
+	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_CAPTURE);
 #endif
 	snd_azf3328_dbgcallleave();
 	return 0;
@@ -923,6 +1096,7 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int result = 0;
 	unsigned int status1;
+	int previously_muted;
 
 	snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd);
 
@@ -930,20 +1104,23 @@
 	case SNDRV_PCM_TRIGGER_START:
 		snd_azf3328_dbgplay("START PLAYBACK\n");
 
-		/* mute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+		/* mute WaveOut (avoid clicking during setup) */
+		previously_muted =
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
 
-		snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+		snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
 			runtime->rate,
 			snd_pcm_format_width(runtime->format),
 			runtime->channels);
 
 		spin_lock(&chip->reg_lock);
-		/* stop playback */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+
+		/* stop playback */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
-	    
+
 		/* FIXME: clear interrupts or what??? */
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_IRQTYPE, 0xffff);
 		spin_unlock(&chip->reg_lock);
@@ -951,7 +1128,7 @@
 		snd_azf3328_setdmaa(chip, runtime->dma_addr,
 			snd_pcm_lib_period_bytes(substream),
 			snd_pcm_lib_buffer_bytes(substream),
-			0);
+			AZF_PLAYBACK);
 
 		spin_lock(&chip->reg_lock);
 #ifdef WIN9X
@@ -978,30 +1155,35 @@
 			DMA_SOMETHING_ELSE);
 #endif
 		spin_unlock(&chip->reg_lock);
+		snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 1);
 
 		/* now unmute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+		if (!previously_muted)
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
 
-		chip->is_playing = 1;
 		snd_azf3328_dbgplay("STARTED PLAYBACK\n");
 		break;
 	case SNDRV_PCM_TRIGGER_RESUME:
 		snd_azf3328_dbgplay("RESUME PLAYBACK\n");
 		/* resume playback if we were active */
-		if (chip->is_playing)
+		spin_lock(&chip->reg_lock);
+		if (chip->audio_stream[AZF_PLAYBACK].running)
 			snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
 				snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
+		spin_unlock(&chip->reg_lock);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		snd_azf3328_dbgplay("STOP PLAYBACK\n");
 
-		/* mute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+		/* mute WaveOut (avoid clicking during setup) */
+		previously_muted =
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
 
 		spin_lock(&chip->reg_lock);
-		/* stop playback */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
 
+		/* stop playback */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
 
@@ -1013,10 +1195,12 @@
 		status1 &= ~DMA_PLAY_SOMETHING1;
 		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
 		spin_unlock(&chip->reg_lock);
-	    
+		snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
+
 		/* now unmute WaveOut */
-		snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
-		chip->is_playing = 0;
+		if (!previously_muted)
+			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+
 		snd_azf3328_dbgplay("STOPPED PLAYBACK\n");
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -1035,7 +1219,7 @@
 		printk(KERN_ERR "FIXME: unknown trigger mode!\n");
                 return -EINVAL;
 	}
-	
+
 	snd_azf3328_dbgcallleave();
 	return result;
 }
@@ -1057,17 +1241,19 @@
 
 		snd_azf3328_dbgplay("START CAPTURE\n");
 
-		snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
+		snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
 			runtime->rate,
 			snd_pcm_format_width(runtime->format),
 			runtime->channels);
 
 		spin_lock(&chip->reg_lock);
-		/* stop recording */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
+
+		/* stop recording */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-	    
+
 		/* FIXME: clear interrupts or what??? */
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_IRQTYPE, 0xffff);
 		spin_unlock(&chip->reg_lock);
@@ -1075,7 +1261,7 @@
 		snd_azf3328_setdmaa(chip, runtime->dma_addr,
 			snd_pcm_lib_period_bytes(substream),
 			snd_pcm_lib_buffer_bytes(substream),
-			1);
+			AZF_CAPTURE);
 
 		spin_lock(&chip->reg_lock);
 #ifdef WIN9X
@@ -1102,24 +1288,27 @@
 			DMA_SOMETHING_ELSE);
 #endif
 		spin_unlock(&chip->reg_lock);
+		snd_azf3328_codec_activity(chip, AZF_CAPTURE, 1);
 
-		chip->is_recording = 1;
 		snd_azf3328_dbgplay("STARTED CAPTURE\n");
 		break;
 	case SNDRV_PCM_TRIGGER_RESUME:
 		snd_azf3328_dbgplay("RESUME CAPTURE\n");
 		/* resume recording if we were active */
-		if (chip->is_recording)
+		spin_lock(&chip->reg_lock);
+		if (chip->audio_stream[AZF_CAPTURE].running)
 			snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
 				snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
+		spin_unlock(&chip->reg_lock);
 		break;
         case SNDRV_PCM_TRIGGER_STOP:
 		snd_azf3328_dbgplay("STOP CAPTURE\n");
 
 		spin_lock(&chip->reg_lock);
-		/* stop recording */
+		/* first, remember current value: */
 		status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
 
+		/* stop recording */
 		status1 &= ~DMA_RESUME;
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
 
@@ -1129,8 +1318,8 @@
 		status1 &= ~DMA_PLAY_SOMETHING1;
 		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
 		spin_unlock(&chip->reg_lock);
-	    
-		chip->is_recording = 0;
+		snd_azf3328_codec_activity(chip, AZF_CAPTURE, 0);
+
 		snd_azf3328_dbgplay("STOPPED CAPTURE\n");
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -1149,7 +1338,7 @@
 		printk(KERN_ERR "FIXME: unknown trigger mode!\n");
                 return -EINVAL;
 	}
-	
+
 	snd_azf3328_dbgcallleave();
 	return result;
 }
@@ -1162,11 +1351,11 @@
 	snd_pcm_uframes_t frmres;
 
 #ifdef QUERY_HARDWARE
-	bufptr = inl(chip->codec_port+IDX_IO_PLAY_DMA_START_1);
+	bufptr = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_START_1);
 #else
 	bufptr = substream->runtime->dma_addr;
 #endif
-	result = inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS);
+	result = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_CURRPOS);
 
 	/* calculate offset */
 	result -= bufptr;
@@ -1183,11 +1372,11 @@
 	snd_pcm_uframes_t frmres;
 
 #ifdef QUERY_HARDWARE
-	bufptr = inl(chip->codec_port+IDX_IO_REC_DMA_START_1);
+	bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1);
 #else
 	bufptr = substream->runtime->dma_addr;
 #endif
-	result = inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS);
+	result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS);
 
 	/* calculate offset */
 	result -= bufptr;
@@ -1196,27 +1385,241 @@
 	return frmres;
 }
 
+/******************************************************************/
+
+#ifdef SUPPORT_GAMEPORT
+static inline void
+snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_io_reg_setb(
+		chip->game_io+IDX_GAME_HWCONFIG,
+		GAME_HWCFG_IRQ_ENABLE,
+		enable
+	);
+}
+
+static inline void
+snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_io_reg_setb(
+		chip->game_io+IDX_GAME_HWCONFIG,
+		GAME_HWCFG_LEGACY_ADDRESS_ENABLE,
+		enable
+	);
+}
+
+static inline void
+snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable)
+{
+	snd_azf3328_codec_reg_6AH_update(
+		chip, IO_6A_SOMETHING2_GAMEPORT, enable
+	);
+}
+
+static inline void
+snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
+{
+	/*
+	 * skeleton handler only
+	 * (we do not want axis reading in interrupt handler - too much load!)
+	 */
+	snd_azf3328_dbggame("gameport irq\n");
+
+	 /* this should ACK the gameport IRQ properly, hopefully. */
+	snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
+}
+
+static int
+snd_azf3328_gameport_open(struct gameport *gameport, int mode)
+{
+	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+	int res;
+
+	snd_azf3328_dbggame("gameport_open, mode %d\n", mode);
+	switch (mode) {
+	case GAMEPORT_MODE_COOKED:
+	case GAMEPORT_MODE_RAW:
+		res = 0;
+		break;
+	default:
+		res = -1;
+		break;
+	}
+
+	snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
+
+	return res;
+}
+
+static void
+snd_azf3328_gameport_close(struct gameport *gameport)
+{
+	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+
+	snd_azf3328_dbggame("gameport_close\n");
+	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
+}
+
+static int
+snd_azf3328_gameport_cooked_read(struct gameport *gameport,
+				 int *axes,
+				 int *buttons
+)
+{
+	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
+	int i;
+	u8 val;
+	unsigned long flags;
+
+	snd_assert(chip, return 0);
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
+	*buttons = (~(val) >> 4) & 0xf;
+
+	/* ok, this one is a bit dirty: cooked_read is being polled by a timer,
+	 * thus we're atomic and cannot actively wait in here
+	 * (which would be useful for us since it probably would be better
+	 * to trigger a measurement in here, then wait a short amount of
+	 * time until it's finished, then read values of _this_ measurement).
+	 *
+	 * Thus we simply resort to reading values if they're available already
+	 * and trigger the next measurement.
+	 */
+
+	val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
+	if (val & GAME_AXES_SAMPLING_READY) {
+		for (i = 0; i < 4; ++i) {
+			/* configure the axis to read */
+			val = (i << 4) | 0x0f;
+			snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
+
+			chip->axes[i] = snd_azf3328_game_inw(
+						chip, IDX_GAME_AXIS_VALUE
+					);
+		}
+	}
+
+	/* trigger next axes sampling, to be evaluated the next time we
+	 * enter this function */
+
+	/* for some very, very strange reason we cannot enable
+	 * Measurement Ready monitoring for all axes here,
+	 * at least not when only one joystick connected */
+	val = 0x03; /* we're able to monitor axes 1 and 2 only */
+	snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
+
+	snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	for (i = 0; i < 4; i++) {
+		axes[i] = chip->axes[i];
+		if (axes[i] == 0xffff)
+			axes[i] = -1;
+	}
+
+	snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n",
+		axes[0], axes[1], axes[2], axes[3], *buttons
+	);
+
+	return 0;
+}
+
+static int __devinit
+snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
+{
+	struct gameport *gp;
+
+	chip->gameport = gp = gameport_allocate_port();
+	if (!gp) {
+		printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n");
+		return -ENOMEM;
+	}
+
+	gameport_set_name(gp, "AZF3328 Gameport");
+	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
+	gameport_set_dev_parent(gp, &chip->pci->dev);
+	gp->io = chip->game_io;
+	gameport_set_port_data(gp, chip);
+
+	gp->open = snd_azf3328_gameport_open;
+	gp->close = snd_azf3328_gameport_close;
+	gp->fuzz = 16; /* seems ok */
+	gp->cooked_read = snd_azf3328_gameport_cooked_read;
+
+	/* DISABLE legacy address: we don't need it! */
+	snd_azf3328_gameport_legacy_address_enable(chip, 0);
+
+	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
+
+	gameport_register_port(chip->gameport);
+
+	return 0;
+}
+
+static void
+snd_azf3328_gameport_free(struct snd_azf3328 *chip)
+{
+	if (chip->gameport) {
+		gameport_unregister_port(chip->gameport);
+		chip->gameport = NULL;
+	}
+	snd_azf3328_gameport_irq_enable(chip, 0);
+}
+#else
+static inline int
+snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
+static inline void
+snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
+static inline void
+snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
+{
+	printk(KERN_WARNING "huh, game port IRQ occurred!?\n");
+}
+#endif /* SUPPORT_GAMEPORT */
+
+/******************************************************************/
+
+static inline void
+snd_azf3328_irq_log_unknown_type(u8 which)
+{
+	snd_azf3328_dbgplay(
+	"azt3328: unknown IRQ type (%x) occurred, please report!\n",
+		which
+	);
+}
+
 static irqreturn_t
 snd_azf3328_interrupt(int irq, void *dev_id)
 {
 	struct snd_azf3328 *chip = dev_id;
 	u8 status, which;
+#if DEBUG_PLAY_REC
 	static unsigned long irq_count;
+#endif
 
 	status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS);
 
         /* fast path out, to ease interrupt sharing */
-	if (!(status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_MPU401|IRQ_TIMER)))
+	if (!(status &
+		(IRQ_PLAYBACK|IRQ_RECORDING|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
+	))
 		return IRQ_NONE; /* must be interrupt for another device */
 
-	snd_azf3328_dbgplay("Interrupt %ld!\nIDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
-		irq_count,
-		snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
-		snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
-		status);
-		
+	snd_azf3328_dbgplay(
+		"irq_count %ld! IDX_IO_PLAY_FLAGS %04x, "
+		"IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
+			irq_count++ /* debug-only */,
+			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
+			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
+			status
+	);
+
 	if (status & IRQ_TIMER) {
-		/* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */
+		/* snd_azf3328_dbgplay("timer %ld\n",
+			snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
+				& TIMER_VALUE_MASK
+		); */
 		if (chip->timer)
 			snd_timer_interrupt(chip->timer, chip->timer->sticks);
 		/* ACK timer */
@@ -1232,15 +1635,20 @@
 		snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
                	spin_unlock(&chip->reg_lock);
 
-		if (chip->pcm && chip->playback_substream) {
-			snd_pcm_period_elapsed(chip->playback_substream);
+		if (chip->pcm && chip->audio_stream[AZF_PLAYBACK].substream) {
+			snd_pcm_period_elapsed(
+				chip->audio_stream[AZF_PLAYBACK].substream
+			);
 			snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
 				which,
-				inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS));
+				snd_azf3328_codec_inl(
+					chip, IDX_IO_PLAY_DMA_CURRPOS
+				)
+			);
 		} else
-			snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
+			printk(KERN_WARNING "azt3328: irq handler problem!\n");
 		if (which & IRQ_PLAY_SOMETHING)
-			snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n");
+			snd_azf3328_irq_log_unknown_type(which);
 	}
 	if (status & IRQ_RECORDING) {
                 spin_lock(&chip->reg_lock);
@@ -1249,16 +1657,23 @@
 		snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
 		spin_unlock(&chip->reg_lock);
 
-		if (chip->pcm && chip->capture_substream) {
-			snd_pcm_period_elapsed(chip->capture_substream);
+		if (chip->pcm && chip->audio_stream[AZF_CAPTURE].substream) {
+			snd_pcm_period_elapsed(
+				chip->audio_stream[AZF_CAPTURE].substream
+			);
 			snd_azf3328_dbgplay("REC  period done (#%x), @ %x\n",
 				which,
-				inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS));
+				snd_azf3328_codec_inl(
+					chip, IDX_IO_REC_DMA_CURRPOS
+				)
+			);
 		} else
-			snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n");
+			printk(KERN_WARNING "azt3328: irq handler problem!\n");
 		if (which & IRQ_REC_SOMETHING)
-			snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n");
+			snd_azf3328_irq_log_unknown_type(which);
 	}
+	if (status & IRQ_GAMEPORT)
+		snd_azf3328_gameport_interrupt(chip);
 	/* MPU401 has less critical IRQ requirements
 	 * than timer and playback/recording, right? */
 	if (status & IRQ_MPU401) {
@@ -1268,7 +1683,6 @@
 		 * If so, then I don't know how... */
 		snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n");
 	}
-	irq_count++;
 	return IRQ_HANDLED;
 }
 
@@ -1287,8 +1701,8 @@
 	.rates =		SNDRV_PCM_RATE_5512 |
 				SNDRV_PCM_RATE_8000_48000 |
 				SNDRV_PCM_RATE_KNOT,
-	.rate_min =		4000,
-	.rate_max =		66200,
+	.rate_min =		AZF_FREQ_4000,
+	.rate_max =		AZF_FREQ_66200,
 	.channels_min =		1,
 	.channels_max =		2,
 	.buffer_bytes_max =	65536,
@@ -1315,8 +1729,8 @@
 	.rates =		SNDRV_PCM_RATE_5512 |
 				SNDRV_PCM_RATE_8000_48000 |
 				SNDRV_PCM_RATE_KNOT,
-	.rate_min =		4000,
-	.rate_max =		66200,
+	.rate_min =		AZF_FREQ_4000,
+	.rate_max =		AZF_FREQ_66200,
 	.channels_min =		1,
 	.channels_max =		2,
 	.buffer_bytes_max =	65536,
@@ -1329,10 +1743,24 @@
 
 
 static unsigned int snd_azf3328_fixed_rates[] = {
-	4000, 4800, 5512, 6620, 8000, 9600, 11025, 13240, 16000, 22050, 32000,
-	44100, 48000, 66200 };
+	AZF_FREQ_4000,
+	AZF_FREQ_4800,
+	AZF_FREQ_5512,
+	AZF_FREQ_6620,
+	AZF_FREQ_8000,
+	AZF_FREQ_9600,
+	AZF_FREQ_11025,
+	AZF_FREQ_13240,
+	AZF_FREQ_16000,
+	AZF_FREQ_22050,
+	AZF_FREQ_32000,
+	AZF_FREQ_44100,
+	AZF_FREQ_48000,
+	AZF_FREQ_66200
+};
+
 static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
-	.count = ARRAY_SIZE(snd_azf3328_fixed_rates), 
+	.count = ARRAY_SIZE(snd_azf3328_fixed_rates),
 	.list = snd_azf3328_fixed_rates,
 	.mask = 0,
 };
@@ -1346,7 +1774,7 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	snd_azf3328_dbgcallenter();
-	chip->playback_substream = substream;
+	chip->audio_stream[AZF_PLAYBACK].substream = substream;
 	runtime->hw = snd_azf3328_playback;
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &snd_azf3328_hw_constraints_rates);
@@ -1361,7 +1789,7 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	snd_azf3328_dbgcallenter();
-	chip->capture_substream = substream;
+	chip->audio_stream[AZF_CAPTURE].substream = substream;
 	runtime->hw = snd_azf3328_capture;
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &snd_azf3328_hw_constraints_rates);
@@ -1375,7 +1803,7 @@
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 
 	snd_azf3328_dbgcallenter();
-	chip->playback_substream = NULL;
+	chip->audio_stream[AZF_PLAYBACK].substream = NULL;
 	snd_azf3328_dbgcallleave();
 	return 0;
 }
@@ -1386,7 +1814,7 @@
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 
 	snd_azf3328_dbgcallenter();
-	chip->capture_substream = NULL;
+	chip->audio_stream[AZF_CAPTURE].substream = NULL;
 	snd_azf3328_dbgcallleave();
 	return 0;
 }
@@ -1441,102 +1869,8 @@
 
 /******************************************************************/
 
-#ifdef SUPPORT_JOYSTICK
-static int __devinit
-snd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev)
-{
-	struct gameport *gp;
-	struct resource *r;
-
-	if (!joystick[dev])
-		return -ENODEV;
-
-	if (!(r = request_region(0x200, 8, "AZF3328 gameport"))) {
-		printk(KERN_WARNING "azt3328: cannot reserve joystick ports\n");
-		return -EBUSY;
-	}
-
-	chip->gameport = gp = gameport_allocate_port();
-	if (!gp) {
-		printk(KERN_ERR "azt3328: cannot allocate memory for gameport\n");
-		release_and_free_resource(r);
-		return -ENOMEM;
-	}
-
-	gameport_set_name(gp, "AZF3328 Gameport");
-	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
-	gameport_set_dev_parent(gp, &chip->pci->dev);
-	gp->io = 0x200;
-	gameport_set_port_data(gp, r);
-
-	snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
-			      snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) | LEGACY_JOY);
-
-	gameport_register_port(chip->gameport);
-
-	return 0;
-}
-
-static void
-snd_azf3328_free_joystick(struct snd_azf3328 *chip)
-{
-	if (chip->gameport) {
-		struct resource *r = gameport_get_port_data(chip->gameport);
-
-		gameport_unregister_port(chip->gameport);
-		chip->gameport = NULL;
-		/* disable gameport */
-		snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
-				      snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
-		release_and_free_resource(r);
-	}
-}
-#else
-static inline int
-snd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }
-static inline void
-snd_azf3328_free_joystick(struct snd_azf3328 *chip) { }
-#endif
-
-/******************************************************************/
-
-static int
-snd_azf3328_free(struct snd_azf3328 *chip)
-{
-        if (chip->irq < 0)
-                goto __end_hw;
-
-	/* reset (close) mixer */
-	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); /* first mute master volume */
-	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
-
-        /* interrupt setup - mask everything (FIXME!) */
-	/* well, at least we know how to disable the timer IRQ */
-	snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00);
-
-	if (chip->irq >= 0)
-        	synchronize_irq(chip->irq);
-__end_hw:
-	snd_azf3328_free_joystick(chip);
-        if (chip->irq >= 0)
-		free_irq(chip->irq, chip);
-	pci_release_regions(chip->pci);
-	pci_disable_device(chip->pci);
-
-        kfree(chip);
-        return 0;
-}
-
-static int
-snd_azf3328_dev_free(struct snd_device *device)
-{
-	struct snd_azf3328 *chip = device->device_data;
-	return snd_azf3328_free(chip);
-}
-
-/******************************************************************/
-
-/*** NOTE: the physical timer resolution actually is 1024000 ticks per second,
+/*** NOTE: the physical timer resolution actually is 1024000 ticks per second
+ *** (probably derived from main crystal via a divider of 24),
  *** but announcing those attributes to user-space would make programs
  *** configure the timer to a 1 tick value, resulting in an absolutely fatal
  *** timer IRQ storm.
@@ -1564,7 +1898,7 @@
 		delay = 49; /* minimum time is 49 ticks */
 	}
 	snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
-	delay |= TIMER_ENABLE_COUNTDOWN | TIMER_ENABLE_IRQ;
+	delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -1582,7 +1916,7 @@
 	chip = snd_timer_chip(timer);
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* disable timer countdown and interrupt */
-	/* FIXME: should we write TIMER_ACK_IRQ here? */
+	/* FIXME: should we write TIMER_IRQ_ACK here? */
 	snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	snd_azf3328_dbgcallleave();
@@ -1626,9 +1960,10 @@
 
 	snd_azf3328_timer_hw.resolution *= seqtimer_scaling;
 	snd_azf3328_timer_hw.ticks /= seqtimer_scaling;
-	if ((err = snd_timer_new(chip->card, "AZF3328", &tid, &timer)) < 0) {
+
+	err = snd_timer_new(chip->card, "AZF3328", &tid, &timer);
+	if (err < 0)
 		goto out;
-	}
 
 	strcpy(timer->name, "AZF3328 timer");
 	timer->private_data = chip;
@@ -1636,6 +1971,8 @@
 
 	chip->timer = timer;
 
+	snd_azf3328_timer_stop(timer);
+
 	err = 0;
 
 out:
@@ -1645,10 +1982,44 @@
 
 /******************************************************************/
 
+static int
+snd_azf3328_free(struct snd_azf3328 *chip)
+{
+	if (chip->irq < 0)
+		goto __end_hw;
+
+	/* reset (close) mixer:
+	 * first mute master volume, then reset
+	 */
+	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
+	snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+
+	snd_azf3328_timer_stop(chip->timer);
+	snd_azf3328_gameport_free(chip);
+
+	if (chip->irq >= 0)
+		synchronize_irq(chip->irq);
+__end_hw:
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+
+	kfree(chip);
+	return 0;
+}
+
+static int
+snd_azf3328_dev_free(struct snd_device *device)
+{
+	struct snd_azf3328 *chip = device->device_data;
+	return snd_azf3328_free(chip);
+}
+
 #if 0
 /* check whether a bit can be modified */
 static void
-snd_azf3328_test_bit(unsigned int reg, int bit)
+snd_azf3328_test_bit(unsigned unsigned reg, int bit)
 {
 	unsigned char val, valoff, valon;
 
@@ -1659,42 +2030,74 @@
 
 	outb(val|(1 << bit), reg);
 	valon = inb(reg);
-	
+
 	outb(val, reg);
 
-	printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n", reg, bit, val, valoff, valon);
+	printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n",
+				reg, bit, val, valoff, valon
+	);
 }
 #endif
 
-#if DEBUG_MISC
-static void
+static inline void
 snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
 {
+#if DEBUG_MISC
 	u16 tmp;
 
-	snd_azf3328_dbgmisc("codec_port 0x%lx, io2_port 0x%lx, mpu_port 0x%lx, synth_port 0x%lx, mixer_port 0x%lx, irq %d\n", chip->codec_port, chip->io2_port, chip->mpu_port, chip->synth_port, chip->mixer_port, chip->irq);
+	snd_azf3328_dbgmisc(
+		"codec_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
+		"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
+		chip->codec_io, chip->game_io, chip->mpu_io,
+		chip->opl3_io, chip->mixer_io, chip->irq
+	);
 
-	snd_azf3328_dbgmisc("io2 %02x %02x %02x %02x %02x %02x\n", snd_azf3328_io2_inb(chip, 0), snd_azf3328_io2_inb(chip, 1), snd_azf3328_io2_inb(chip, 2), snd_azf3328_io2_inb(chip, 3), snd_azf3328_io2_inb(chip, 4), snd_azf3328_io2_inb(chip, 5));
+	snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
+		snd_azf3328_game_inb(chip, 0),
+		snd_azf3328_game_inb(chip, 1),
+		snd_azf3328_game_inb(chip, 2),
+		snd_azf3328_game_inb(chip, 3),
+		snd_azf3328_game_inb(chip, 4),
+		snd_azf3328_game_inb(chip, 5)
+	);
 
-	for (tmp=0; tmp <= 0x01; tmp += 1)
-		snd_azf3328_dbgmisc("0x%02x: opl 0x%04x, mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, mpu330 0x%04x\n", tmp, inb(0x388 + tmp), inb(0x300 + tmp), inb(0x310 + tmp), inb(0x320 + tmp), inb(0x330 + tmp));
+	for (tmp = 0; tmp < 0x07; tmp += 1)
+		snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
+
+	for (tmp = 0; tmp <= 0x07; tmp += 1)
+		snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n",
+			tmp, inb(0x200 + tmp), inb(0x208 + tmp));
+
+	for (tmp = 0; tmp <= 0x01; tmp += 1)
+		snd_azf3328_dbgmisc(
+			"0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
+			"mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
+				tmp,
+				inb(0x300 + tmp),
+				inb(0x310 + tmp),
+				inb(0x320 + tmp),
+				inb(0x330 + tmp),
+				inb(0x388 + tmp),
+				inb(0x38c + tmp)
+		);
 
 	for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2)
-		snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n", tmp, snd_azf3328_codec_inw(chip, tmp));
+		snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n",
+			tmp, snd_azf3328_codec_inw(chip, tmp)
+		);
 
 	for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
-		snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n", tmp, snd_azf3328_mixer_inw(chip, tmp));
+		snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n",
+			tmp, snd_azf3328_mixer_inw(chip, tmp)
+		);
+#endif /* DEBUG_MISC */
 }
-#else
-static inline void
-snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip) {}
-#endif
 
 static int __devinit
 snd_azf3328_create(struct snd_card *card,
-                                         struct pci_dev *pci,
-                                         unsigned long device_type,
-                                         struct snd_azf3328 ** rchip)
+		   struct pci_dev *pci,
+		   unsigned long device_type,
+		   struct snd_azf3328 **rchip)
 {
 	struct snd_azf3328 *chip;
 	int err;
@@ -1705,7 +2108,8 @@
 
 	*rchip = NULL;
 
-	if ((err = pci_enable_device(pci)) < 0)
+	err = pci_enable_device(pci);
+	if (err < 0)
 		return err;
 
 	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
@@ -1721,20 +2125,25 @@
 	/* check if we can restrict PCI DMA transfers to 24 bits */
 	if (pci_set_dma_mask(pci, DMA_24BIT_MASK) < 0 ||
 	    pci_set_consistent_dma_mask(pci, DMA_24BIT_MASK) < 0) {
-		snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
+		snd_printk(KERN_ERR "architecture does not support "
+					"24bit PCI busmaster DMA\n"
+		);
 		err = -ENXIO;
 		goto out_err;
 	}
 
-	if ((err = pci_request_regions(pci, "Aztech AZF3328")) < 0) {
+	err = pci_request_regions(pci, "Aztech AZF3328");
+	if (err < 0)
 		goto out_err;
-	}
 
-	chip->codec_port = pci_resource_start(pci, 0);
-	chip->io2_port   = pci_resource_start(pci, 1);
-	chip->mpu_port   = pci_resource_start(pci, 2);
-	chip->synth_port = pci_resource_start(pci, 3);
-	chip->mixer_port = pci_resource_start(pci, 4);
+	chip->codec_io = pci_resource_start(pci, 0);
+	chip->game_io  = pci_resource_start(pci, 1);
+	chip->mpu_io   = pci_resource_start(pci, 2);
+	chip->opl3_io = pci_resource_start(pci, 3);
+	chip->mixer_io = pci_resource_start(pci, 4);
+
+	chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00;
+	chip->audio_stream[AZF_CAPTURE].portbase   = chip->codec_io + 0x20;
 
 	if (request_irq(pci->irq, snd_azf3328_interrupt,
 			IRQF_SHARED, card->shortname, chip)) {
@@ -1747,29 +2156,29 @@
 	synchronize_irq(chip->irq);
 
 	snd_azf3328_debug_show_ports(chip);
-	
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0)
 		goto out_err;
-	}
 
 	/* create mixer interface & switches */
-	if ((err = snd_azf3328_mixer_new(chip)) < 0)
+	err = snd_azf3328_mixer_new(chip);
+	if (err < 0)
 		goto out_err;
 
-#if 0
-	/* set very low bitrate to reduce noise and power consumption? */
-	snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, 5512, 8, 1);
-#endif
+	/* shutdown codecs to save power */
+		/* have snd_azf3328_codec_activity() act properly */
+	chip->audio_stream[AZF_PLAYBACK].running = 1;
+	snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
 
 	/* standard chip init stuff */
-	/* default IRQ init value */
+		/* default IRQ init value */
 	tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
 
 	spin_lock_irq(&chip->reg_lock);
 	snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp);
 	snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp);
 	snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp);
-	snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00); /* disable timer */
 	spin_unlock_irq(&chip->reg_lock);
 
 	snd_card_set_dev(card, &pci->dev);
@@ -1805,52 +2214,61 @@
 		return -ENOENT;
 	}
 
-	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0 );
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
 	if (card == NULL)
 		return -ENOMEM;
 
 	strcpy(card->driver, "AZF3328");
 	strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
 
-        if ((err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip)) < 0) {
+	err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
+	if (err < 0)
 		goto out_err;
-	}
 
 	card->private_data = chip;
 
-	if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401,
-				        chip->mpu_port, MPU401_INFO_INTEGRATED,
-					pci->irq, 0, &chip->rmidi)) < 0) {
-		snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
+	err = snd_mpu401_uart_new(
+		card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED,
+		pci->irq, 0, &chip->rmidi
+	);
+	if (err < 0) {
+		snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
+				chip->mpu_io
+		);
 		goto out_err;
 	}
 
-	if ((err = snd_azf3328_timer(chip, 0)) < 0) {
+	err = snd_azf3328_timer(chip, 0);
+	if (err < 0)
 		goto out_err;
-	}
 
-	if ((err = snd_azf3328_pcm(chip, 0)) < 0) {
+	err = snd_azf3328_pcm(chip, 0);
+	if (err < 0)
 		goto out_err;
-	}
 
-	if (snd_opl3_create(card, chip->synth_port, chip->synth_port+2,
+	if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
 			    OPL3_HW_AUTO, 1, &opl3) < 0) {
 		snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
-			   chip->synth_port, chip->synth_port+2 );
+			   chip->opl3_io, chip->opl3_io+2
+		);
 	} else {
-		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+		/* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
+		err = snd_opl3_timer_new(opl3, 1, 2);
+		if (err < 0)
 			goto out_err;
-		}
+		err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+		if (err < 0)
+			goto out_err;
 	}
 
 	opl3->private_data = chip;
 
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
-		card->shortname, chip->codec_port, chip->irq);
+		card->shortname, chip->codec_io, chip->irq);
 
-	if ((err = snd_card_register(card)) < 0) {
+	err = snd_card_register(card);
+	if (err < 0)
 		goto out_err;
-	}
 
 #ifdef MODULE
 	printk(
@@ -1861,19 +2279,18 @@
 	1024000 / seqtimer_scaling, seqtimer_scaling);
 #endif
 
-	if (snd_azf3328_config_joystick(chip, dev) < 0)
-		snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR,
-			      snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY);
+	snd_azf3328_gameport(chip, dev);
 
 	pci_set_drvdata(pci, card);
 	dev++;
 
 	err = 0;
 	goto out;
-	
+
 out_err:
+	snd_printk(KERN_ERR "azf3328: something failed, exiting\n");
 	snd_card_free(card);
-	
+
 out:
 	snd_azf3328_dbgcallleave();
 	return err;
@@ -1894,27 +2311,31 @@
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_azf3328 *chip = card->private_data;
-	int reg;
+	unsigned reg;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-	
+
 	snd_pcm_suspend_all(chip->pcm);
 
-	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
-		chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
+		chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2);
 
 	/* make sure to disable master volume etc. to prevent looping sound */
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
-	
-	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
-		chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
-		chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
-		chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
-		chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2);
+
+	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
+		chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2);
+
+	/* manually store the one currently relevant write-only reg, too */
+	chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH;
+
+	for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
+		chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
+		chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
+		chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2);
 
 	pci_disable_device(pci);
 	pci_save_state(pci);
@@ -1927,7 +2348,7 @@
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_azf3328 *chip = card->private_data;
-	int reg;
+	unsigned reg;
 
 	pci_set_power_state(pci, PCI_D0);
 	pci_restore_state(pci);
@@ -1939,23 +2360,21 @@
 	}
 	pci_set_master(pci);
 
-	for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++)
-		outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++)
-		outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++)
-		outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++)
-		outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++)
-		outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
+		outw(chip->saved_regs_game[reg], chip->game_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
+		outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
+		outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
+		outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2);
+	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
+		outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2);
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
 }
-#endif
-
-
+#endif /* CONFIG_PM */
 
 
 static struct pci_driver driver = {
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h
index 679fa99..7e3e894 100644
--- a/sound/pci/azt3328.h
+++ b/sound/pci/azt3328.h
@@ -1,7 +1,8 @@
 #ifndef __SOUND_AZT3328_H
 #define __SOUND_AZT3328_H
 
-/* "PU" == "power-up value", as tested on PCI168 PCI rev. 10 */
+/* "PU" == "power-up value", as tested on PCI168 PCI rev. 10
+ * "WRITE_ONLY"  == register does not indicate actual bit values */
 
 /*** main I/O area port indices ***/
 /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
@@ -54,7 +55,10 @@
   #define SOUNDFORMAT_XTAL1		0x00
   #define SOUNDFORMAT_XTAL2		0x01
     /* all _SUSPECTED_ values are not used by Windows drivers, so we don't
-     * have any hard facts, only rough measurements */
+     * have any hard facts, only rough measurements.
+     * All we know is that the crystal used on the board has 24.576MHz,
+     * like many soundcards (which results in the frequencies below when
+     * using certain divider values selected by the values below) */
     #define SOUNDFORMAT_FREQ_SUSPECTED_4000	0x0c | SOUNDFORMAT_XTAL1
     #define SOUNDFORMAT_FREQ_SUSPECTED_4800	0x0a | SOUNDFORMAT_XTAL1
     #define SOUNDFORMAT_FREQ_5510		0x0c | SOUNDFORMAT_XTAL2
@@ -72,6 +76,26 @@
   #define SOUNDFORMAT_FLAG_16BIT	0x0010
   #define SOUNDFORMAT_FLAG_2CHANNELS	0x0020
 
+/* define frequency helpers, for maximum value safety */
+enum azf_freq_t {
+#define AZF_FREQ(rate) AZF_FREQ_##rate = rate
+  AZF_FREQ(4000),
+  AZF_FREQ(4800),
+  AZF_FREQ(5512),
+  AZF_FREQ(6620),
+  AZF_FREQ(8000),
+  AZF_FREQ(9600),
+  AZF_FREQ(11025),
+  AZF_FREQ(13240),
+  AZF_FREQ(16000),
+  AZF_FREQ(22050),
+  AZF_FREQ(32000),
+  AZF_FREQ(44100),
+  AZF_FREQ(48000),
+  AZF_FREQ(66200),
+#undef AZF_FREQ
+} AZF_FREQUENCIES;
+
 /** recording area (see also: playback bit flag definitions) **/
 #define IDX_IO_REC_FLAGS	0x20 /* ??, PU:0x0000 */
 #define IDX_IO_REC_IRQTYPE	0x22 /* ??, PU:0x0000 */
@@ -97,40 +121,171 @@
 
 /** DirectX timer, main interrupt area (FIXME: and something else?) **/ 
 #define IDX_IO_TIMER_VALUE	0x60 /* found this timer area by pure luck :-) */
-  #define TIMER_VALUE_MASK		0x000fffffUL /* timer countdown value; triggers IRQ when timer is finished */
-  #define TIMER_ENABLE_COUNTDOWN	0x01000000UL /* activate the timer countdown */
-  #define TIMER_ENABLE_IRQ		0x02000000UL /* trigger timer IRQ on zero transition */
-  #define TIMER_ACK_IRQ			0x04000000UL /* being set in IRQ handler in case port 0x00 (hmm, not port 0x64!?!?) had 0x0020 set upon IRQ handler */
+  /* timer countdown value; triggers IRQ when timer is finished */
+  #define TIMER_VALUE_MASK		0x000fffffUL
+  /* activate timer countdown */
+  #define TIMER_COUNTDOWN_ENABLE	0x01000000UL
+  /* trigger timer IRQ on zero transition */
+  #define TIMER_IRQ_ENABLE		0x02000000UL
+  /* being set in IRQ handler in case port 0x00 (hmm, not port 0x64!?!?)
+   * had 0x0020 set upon IRQ handler */
+  #define TIMER_IRQ_ACK			0x04000000UL
 #define IDX_IO_IRQSTATUS        0x64
-  #define IRQ_PLAYBACK			0x0001
-  #define IRQ_RECORDING			0x0002
-  #define IRQ_MPU401			0x0010
-  #define IRQ_TIMER			0x0020 /* DirectX timer */
-  #define IRQ_UNKNOWN1			0x0040 /* probably unused, or possibly I2S port? or gameport IRQ? */
-  #define IRQ_UNKNOWN2			0x0080 /* probably unused, or possibly I2S port? or gameport IRQ? */
+  /* some IRQ bit in here might also be used to signal a power-management timer
+   * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
+   * Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which
+   * can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */
+
+  #define IRQ_PLAYBACK	0x0001
+  #define IRQ_RECORDING	0x0002
+  #define IRQ_UNKNOWN1	0x0004 /* most probably I2S port */
+  #define IRQ_GAMEPORT	0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
+  #define IRQ_MPU401	0x0010
+  #define IRQ_TIMER	0x0020 /* DirectX timer */
+  #define IRQ_UNKNOWN2	0x0040 /* probably unused, or possibly I2S port? */
+  #define IRQ_UNKNOWN3	0x0080 /* probably unused, or possibly I2S port? */
 #define IDX_IO_66H		0x66    /* writing 0xffff returns 0x0000 */
-#define IDX_IO_SOME_VALUE	0x68	/* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */
-#define IDX_IO_6AH		0x6A	/* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */
-  #define IO_6A_PAUSE_PLAYBACK		0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */
-#define IDX_IO_6CH		0x6C
-#define IDX_IO_6EH		0x6E	/* writing 0xffff returns 0x83fe */
-/* further I/O indices not saved/restored, so probably not used */
+  /* this is set to e.g. 0x3ff or 0x300, and writable;
+   * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
+#define IDX_IO_SOME_VALUE	0x68
+  #define IO_68_RANDOM_TOGGLE1	0x0100	/* toggles randomly */
+  #define IO_68_RANDOM_TOGGLE2	0x0200	/* toggles randomly */
+  /* umm, nope, behaviour of these bits changes depending on what we wrote
+   * to 0x6b!!
+   * And they change upon playback/stop, too:
+   * Writing a value to 0x68 will display this exact value during playback,
+   * too but when stopped it can fall back to a rather different
+   * seemingly random value). Hmm, possibly this is a register which
+   * has a remote shadow which needs proper device supply which only exists
+   * in case playback is active? Or is this driver-induced?
+   */
+
+/* this WORD can be set to have bits 0x0028 activated (FIXME: correct??);
+ * actually inhibits PCM playback!!! maybe power management??: */
+#define IDX_IO_6AH		0x6A /* WRITE_ONLY! */
+  /* bit 5: enabling this will activate permanent counting of bytes 2/3
+   * at gameport I/O (0xb402/3) (equal values each) and cause
+   * gameport legacy I/O at 0x0200 to be _DISABLED_!
+   * Is this Digital Enhanced Game Port Enable??? Or maybe it's Testmode
+   * for Enhanced Digital Gameport (see 4D Wave DX card): */
+  #define IO_6A_SOMETHING1_GAMEPORT	0x0020
+  /* bit 8; sure, this _pauses_ playback (later resumes at same spot!),
+   * but what the heck is this really about??: */
+  #define IO_6A_PAUSE_PLAYBACK_BIT8	0x0100
+  /* bit 9; sure, this _pauses_ playback (later resumes at same spot!),
+   * but what the heck is this really about??: */
+  #define IO_6A_PAUSE_PLAYBACK_BIT9	0x0200
+	/* BIT8 and BIT9 are _NOT_ able to affect OPL3 MIDI playback,
+	 * thus it suggests influence on PCM only!!
+	 * However OTOH there seems to be no bit anywhere around here
+	 * which is able to disable OPL3... */
+  /* bit 10: enabling this actually changes values at legacy gameport
+   * I/O address (0x200); is this enabling of the Digital Enhanced Game Port???
+   * Or maybe this simply switches off the NE558 circuit, since enabling this
+   * still lets us evaluate button states, but not axis states */
+  #define IO_6A_SOMETHING2_GAMEPORT      0x0400
+	/* writing 0x0300: causes quite some crackling during
+	 * PC activity such as switching windows (PCI traffic??
+	 * --> FIFO/timing settings???) */
+	/* writing 0x0100 plus/or 0x0200 inhibits playback */
+	/* since the Windows .INF file has Flag_Enable_JoyStick and
+	 * Flag_Enable_SB_DOS_Emulation directly together, it stands to reason
+	 * that some other bit in this same register might be responsible
+	 * for SB DOS Emulation activation (note that the file did NOT define
+	 * a switch for OPL3!) */
+#define IDX_IO_6CH		0x6C	/* unknown; fully read-writable */
+#define IDX_IO_6EH		0x6E
+	/* writing 0xffff returns 0x83fe (or 0x03fe only).
+	 * writing 0x83 (and only 0x83!!) to 0x6f will cause 0x6c to switch
+	 * from 0000 to ffff. */
+
+/* further I/O indices not saved/restored and not readable after writing,
+ * so probably not used */
 
 
-/*** I/O 2 area port indices ***/
+/*** Gameport area port indices ***/
 /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ 
-#define AZF_IO_SIZE_IO2		0x08
-#define AZF_IO_SIZE_IO2_PM	0x06
+#define AZF_IO_SIZE_GAME		0x08
+#define AZF_IO_SIZE_GAME_PM	0x06
 
-#define IDX_IO2_LEGACY_ADDR	0x04
-  #define LEGACY_SOMETHING		0x01 /* OPL3?? */
-  #define LEGACY_JOY			0x08
+enum {
+	AZF_GAME_LEGACY_IO_PORT = 0x200
+} AZF_GAME_CONFIGS;
 
+#define IDX_GAME_LEGACY_COMPATIBLE	0x00
+	/* in some operation mode, writing anything to this port
+	 * triggers an interrupt:
+	 * yup, that's in case IDX_GAME_01H has one of the
+	 * axis measurement bits enabled
+	 * (and of course one needs to have GAME_HWCFG_IRQ_ENABLE, too) */
+
+#define IDX_GAME_AXES_CONFIG            0x01
+	/* NOTE: layout of this register awfully similar (read: "identical??")
+	 * to AD1815JS.pdf (p.29) */
+
+  /* enables axis 1 (X axis) measurement: */
+  #define GAME_AXES_ENABLE_1		0x01
+  /* enables axis 2 (Y axis) measurement: */
+  #define GAME_AXES_ENABLE_2		0x02
+  /* enables axis 3 (X axis) measurement: */
+  #define GAME_AXES_ENABLE_3		0x04
+  /* enables axis 4 (Y axis) measurement: */
+  #define GAME_AXES_ENABLE_4		0x08
+  /* selects the current axis to read the measured value of
+   * (at IDX_GAME_AXIS_VALUE):
+   * 00 = axis 1, 01 = axis 2, 10 = axis 3, 11 = axis 4: */
+  #define GAME_AXES_READ_MASK		0x30
+  /* enable to have the latch continuously accept ADC values
+   * (and continuously cause interrupts in case interrupts are enabled);
+   * AD1815JS.pdf says it's ~16ms interval there: */
+  #define GAME_AXES_LATCH_ENABLE	0x40
+  /* joystick data (measured axes) ready for reading: */
+  #define GAME_AXES_SAMPLING_READY	0x80
+
+  /* NOTE: other card specs (SiS960 and others!) state that the
+   * game position latches should be frozen when reading and be freed
+   * (== reset?) after reading!!!
+   * Freezing most likely means disabling 0x40 (GAME_AXES_LATCH_ENABLE),
+   *  but how to free the value? */
+  /* An internet search for "gameport latch ADC" should provide some insight
+   * into how to program such a gameport system. */
+
+  /* writing 0xf0 to 01H once reset both counters to 0, in some special mode!?
+   * yup, in case 6AH 0x20 is not enabled
+   * (and 0x40 is sufficient, 0xf0 is not needed) */
+
+#define IDX_GAME_AXIS_VALUE	0x02
+	/* R: value of currently configured axis (word value!);
+	 * W: trigger axis measurement */
+
+#define IDX_GAME_HWCONFIG	0x04
+	/* note: bits 4 to 7 are never set (== 0) when reading!
+	 * --> reserved bits? */
+  /* enables IRQ notification upon axes measurement ready: */
+  #define GAME_HWCFG_IRQ_ENABLE			0x01
+  /* these bits choose a different frequency for the
+   *  internal ADC counter increment.
+   * hmm, seems to be a combo of bits:
+   * 00 --> standard frequency
+   * 10 --> 1/2
+   * 01 --> 1/20
+   * 11 --> 1/200: */
+  #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK	0x06
+
+  /* enable gameport legacy I/O address (0x200)
+   * I was unable to locate any configurability for a different address: */
+  #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE	0x08
+
+/*** MPU401 ***/
 #define AZF_IO_SIZE_MPU		0x04
 #define AZF_IO_SIZE_MPU_PM	0x04
 
-#define AZF_IO_SIZE_SYNTH	0x08
-#define AZF_IO_SIZE_SYNTH_PM	0x06
+/*** OPL3 synth ***/
+#define AZF_IO_SIZE_OPL3	0x08
+#define AZF_IO_SIZE_OPL3_PM	0x06
+/* hmm, given that a standard OPL3 has 4 registers only,
+ * there might be some enhanced functionality lurking at the end
+ * (especially since register 0x04 has a "non-empty" value 0xfe) */
 
 /*** mixer I/O area port indices ***/
 /* (only 0x22 of 0x40 bytes saved/restored by Windows driver)
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index ecbe79b..2f8b28a 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -249,6 +249,11 @@
 	   .name   = "MSI K8N Diamond MB [SB0438]",
 	   .gpio_type = 2,
 	   .i2c_adc = 1 } ,
+	 /* Another MSI K8N Diamond MB, which has apprently a different SSID */
+	 { .serial = 0x10091102,
+	   .name   = "MSI K8N Diamond MB",
+	   .gpio_type = 2,
+	   .i2c_adc = 1 } ,
 	 /* Shuttle XPC SD31P which has an onboard Creative Labs
 	  * Sound Blaster Live! 24-bit EAX
 	  * high-definition 7.1 audio processor".
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 548c9cc..2f283ea 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1528,6 +1528,7 @@
 	 .ca0151_chip = 1,
 	 .spk71 = 1,
 	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
 	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */
 	 .ac97_chip = 1} ,
 	{.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index fd22120..f34bbfb 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1578,6 +1578,10 @@
 		ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
 	else
 		ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
+	if (emu->card_capabilities->invert_shared_spdif)
+		ucontrol->value.integer.value[0] =
+			!ucontrol->value.integer.value[0];
+		
 	return 0;
 }
 
@@ -1586,15 +1590,18 @@
 {
 	unsigned long flags;
 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
-	unsigned int reg, val;
+	unsigned int reg, val, sw;
 	int change = 0;
 
+	sw = ucontrol->value.integer.value[0];
+	if (emu->card_capabilities->invert_shared_spdif)
+		sw = !sw;
 	spin_lock_irqsave(&emu->reg_lock, flags);
 	if ( emu->card_capabilities->i2c_adc) {
 		/* Do nothing for Audigy 2 ZS Notebook */
 	} else if (emu->audigy) {
 		reg = inl(emu->port + A_IOCFG);
-		val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
+		val = sw ? A_IOCFG_GPOUT0 : 0;
 		change = (reg & A_IOCFG_GPOUT0) != val;
 		if (change) {
 			reg &= ~A_IOCFG_GPOUT0;
@@ -1603,7 +1610,7 @@
 		}
 	}
 	reg = inl(emu->port + HCFG);
-	val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0;
+	val = sw ? HCFG_GPOUT0 : 0;
 	change |= (reg & HCFG_GPOUT0) != val;
 	if (change) {
 		reg &= ~HCFG_GPOUT0;
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 916c1db..7d379f5 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -437,43 +437,49 @@
 	*last_page_ret = last_page;
 }
 
+/* release allocated pages */
+static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
+			       int last_page)
+{
+	int page;
+
+	for (page = first_page; page <= last_page; page++) {
+		free_page((unsigned long)emu->page_ptr_table[page]);
+		emu->page_addr_table[page] = 0;
+		emu->page_ptr_table[page] = NULL;
+	}
+}
+
 /*
  * allocate kernel pages
  */
 static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
 {
 	int page, first_page, last_page;
-	struct snd_dma_buffer dmab;
 
 	emu10k1_memblk_init(blk);
 	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
 	/* allocate kernel pages */
 	for (page = first_page; page <= last_page; page++) {
-		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
-					PAGE_SIZE, &dmab) < 0)
-			goto __fail;
-		if (! is_valid_page(emu, dmab.addr)) {
-			snd_dma_free_pages(&dmab);
-			goto __fail;
+		/* first try to allocate from <4GB zone */
+		struct page *p = alloc_page(GFP_KERNEL | GFP_DMA32 |
+					    __GFP_NOWARN);
+		if (!p || (page_to_pfn(p) & ~(emu->dma_mask >> PAGE_SHIFT))) {
+			if (p)
+				__free_page(p);
+			/* try to allocate from <16MB zone */
+			p = alloc_page(GFP_ATOMIC | GFP_DMA |
+				       __GFP_NORETRY | /* no OOM-killer */
+				       __GFP_NOWARN);
 		}
-		emu->page_addr_table[page] = dmab.addr;
-		emu->page_ptr_table[page] = dmab.area;
+		if (!p) {
+			__synth_free_pages(emu, first_page, page - 1);
+			return -ENOMEM;
+		}
+		emu->page_addr_table[page] = page_to_phys(p);
+		emu->page_ptr_table[page] = page_address(p);
 	}
 	return 0;
-
-__fail:
-	/* release allocated pages */
-	last_page = page - 1;
-	for (page = first_page; page <= last_page; page++) {
-		dmab.area = emu->page_ptr_table[page];
-		dmab.addr = emu->page_addr_table[page];
-		dmab.bytes = PAGE_SIZE;
-		snd_dma_free_pages(&dmab);
-		emu->page_addr_table[page] = 0;
-		emu->page_ptr_table[page] = NULL;
-	}
-
-	return -ENOMEM;
 }
 
 /*
@@ -481,23 +487,10 @@
  */
 static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
 {
-	int page, first_page, last_page;
-	struct snd_dma_buffer dmab;
+	int first_page, last_page;
 
 	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
-	dmab.dev.type = SNDRV_DMA_TYPE_DEV;
-	dmab.dev.dev = snd_dma_pci_data(emu->pci);
-	for (page = first_page; page <= last_page; page++) {
-		if (emu->page_ptr_table[page] == NULL)
-			continue;
-		dmab.area = emu->page_ptr_table[page];
-		dmab.addr = emu->page_addr_table[page];
-		dmab.bytes = PAGE_SIZE;
-		snd_dma_free_pages(&dmab);
-		emu->page_addr_table[page] = 0;
-		emu->page_ptr_table[page] = NULL;
-	}
-
+	__synth_free_pages(emu, first_page, last_page);
 	return 0;
 }
 
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a6be6e3..d2e1093 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -2335,7 +2335,7 @@
 	if (!tbl)
 		return -1;
 	if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 		char tmp[10];
 		const char *model = NULL;
 		if (models)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index dcd390b..efc6828 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -78,7 +78,7 @@
 #define AC_VERB_GET_BEEP_CONTROL		0x0f0a
 #define AC_VERB_GET_EAPD_BTLENABLE		0x0f0c
 #define AC_VERB_GET_DIGI_CONVERT_1		0x0f0d
-#define AC_VERB_GET_DIGI_CONVERT_2		0x0f0e
+#define AC_VERB_GET_DIGI_CONVERT_2		0x0f0e /* unused */
 #define AC_VERB_GET_VOLUME_KNOB_CONTROL		0x0f0f
 /* f10-f1a: GPIO */
 #define AC_VERB_GET_GPIO_DATA			0x0f15
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 2177d9a..6e18a42 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -88,7 +88,7 @@
 
 static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
 {
-#ifndef CONFIG_SND_DEBUG_DETECT
+#ifndef CONFIG_SND_DEBUG_VERBOSE
 	if (!capable(CAP_SYS_RAWIO))
 		return -EACCES;
 #endif
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b3a618e..16715a6 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -55,6 +55,7 @@
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
 static char *model[SNDRV_CARDS];
 static int position_fix[SNDRV_CARDS];
+static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int single_cmd;
 static int enable_msi;
@@ -69,7 +70,9 @@
 MODULE_PARM_DESC(model, "Use the given board model.");
 module_param_array(position_fix, int, NULL, 0444);
 MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
-		 "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+		 "(0 = auto, 1 = none, 2 = POSBUF).");
+module_param_array(bdl_pos_adj, int, NULL, 0644);
+MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
 module_param_array(probe_mask, int, NULL, 0444);
 MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
 module_param(single_cmd, bool, 0444);
@@ -197,6 +200,10 @@
 #define ATIHDMI_NUM_CAPTURE	0
 #define ATIHDMI_NUM_PLAYBACK	1
 
+/* TERA has 4 playback and 3 capture */
+#define TERA_NUM_CAPTURE	3
+#define TERA_NUM_PLAYBACK	4
+
 /* this number is statically defined for simplicity */
 #define MAX_AZX_DEV		16
 
@@ -259,9 +266,8 @@
 /* position fix mode */
 enum {
 	POS_FIX_AUTO,
-	POS_FIX_NONE,
+	POS_FIX_LPIB,
 	POS_FIX_POSBUF,
-	POS_FIX_FIFO,
 };
 
 /* Defines for ATI HD Audio support in SB450 south bridge */
@@ -285,6 +291,7 @@
 	u32 *posbuf;		/* position buffer pointer */
 
 	unsigned int bufsize;	/* size of the play buffer in bytes */
+	unsigned int period_bytes; /* size of the period in bytes */
 	unsigned int frags;	/* number for period in the play buffer */
 	unsigned int fifo_size;	/* FIFO size */
 
@@ -301,11 +308,11 @@
 					 */
 	unsigned char stream_tag;	/* assigned stream */
 	unsigned char index;		/* stream index */
-	/* for sanity check of position buffer */
-	unsigned int period_intr;
 
 	unsigned int opened :1;
 	unsigned int running :1;
+	unsigned int irq_pending :1;
+	unsigned int irq_ignore :1;
 };
 
 /* CORB/RIRB */
@@ -323,6 +330,7 @@
 struct azx {
 	struct snd_card *card;
 	struct pci_dev *pci;
+	int dev_index;
 
 	/* chip type specific */
 	int driver_type;
@@ -366,9 +374,13 @@
 	unsigned int single_cmd :1;
 	unsigned int polling_mode :1;
 	unsigned int msi :1;
+	unsigned int irq_pending_warned :1;
 
 	/* for debugging */
 	unsigned int last_cmd;	/* last issued command (to sync) */
+
+	/* for pending irqs */
+	struct work_struct irq_pending_work;
 };
 
 /* driver types */
@@ -381,6 +393,7 @@
 	AZX_DRIVER_SIS,
 	AZX_DRIVER_ULI,
 	AZX_DRIVER_NVIDIA,
+	AZX_DRIVER_TERA,
 };
 
 static char *driver_short_names[] __devinitdata = {
@@ -392,6 +405,7 @@
 	[AZX_DRIVER_SIS] = "HDA SIS966",
 	[AZX_DRIVER_ULI] = "HDA ULI M5461",
 	[AZX_DRIVER_NVIDIA] = "HDA NVidia",
+	[AZX_DRIVER_TERA] = "HDA Teradici", 
 };
 
 /*
@@ -426,11 +440,6 @@
 /* for pcm support */
 #define get_azx_dev(substream) (substream->runtime->private_data)
 
-/* Get the upper 32bit of the given dma_addr_t
- * Compiler should optimize and eliminate the code if dma_addr_t is 32bit
- */
-#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0)
-
 static int azx_acquire_irq(struct azx *chip, int do_disconnect);
 
 /*
@@ -461,7 +470,7 @@
 	chip->corb.addr = chip->rb.addr;
 	chip->corb.buf = (u32 *)chip->rb.area;
 	azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
-	azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr));
+	azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
 
 	/* set the corb size to 256 entries (ULI requires explicitly) */
 	azx_writeb(chip, CORBSIZE, 0x02);
@@ -476,7 +485,7 @@
 	chip->rirb.addr = chip->rb.addr + 2048;
 	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
 	azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
-	azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr));
+	azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
 
 	/* set the rirb size to 256 entries (ULI requires explicitly) */
 	azx_writeb(chip, RIRBSIZE, 0x02);
@@ -847,7 +856,7 @@
 
 	/* program the position buffer */
 	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
-	azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+	azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
 
 	chip->initialized = 1;
 }
@@ -908,6 +917,8 @@
 }
 
 
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
+
 /*
  * interrupt handler
  */
@@ -930,11 +941,23 @@
 		azx_dev = &chip->azx_dev[i];
 		if (status & azx_dev->sd_int_sta_mask) {
 			azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
-			if (azx_dev->substream && azx_dev->running) {
-				azx_dev->period_intr++;
+			if (!azx_dev->substream || !azx_dev->running)
+				continue;
+			/* ignore the first dummy IRQ (due to pos_adj) */
+			if (azx_dev->irq_ignore) {
+				azx_dev->irq_ignore = 0;
+				continue;
+			}
+			/* check whether this IRQ is really acceptable */
+			if (azx_position_ok(chip, azx_dev)) {
+				azx_dev->irq_pending = 0;
 				spin_unlock(&chip->reg_lock);
 				snd_pcm_period_elapsed(azx_dev->substream);
 				spin_lock(&chip->reg_lock);
+			} else {
+				/* bogus IRQ, process it later */
+				azx_dev->irq_pending = 1;
+				schedule_work(&chip->irq_pending_work);
 			}
 		}
 	}
@@ -959,59 +982,107 @@
 
 
 /*
- * set up BDL entries
+ * set up a BDL entry
  */
-static int azx_setup_periods(struct snd_pcm_substream *substream,
-			     struct azx_dev *azx_dev)
+static int setup_bdle(struct snd_pcm_substream *substream,
+		      struct azx_dev *azx_dev, u32 **bdlp,
+		      int ofs, int size, int with_ioc)
 {
 	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+	u32 *bdl = *bdlp;
+
+	while (size > 0) {
+		dma_addr_t addr;
+		int chunk;
+
+		if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+			return -EINVAL;
+
+		addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
+		/* program the address field of the BDL entry */
+		bdl[0] = cpu_to_le32((u32)addr);
+		bdl[1] = cpu_to_le32(upper_32_bits(addr));
+		/* program the size field of the BDL entry */
+		chunk = PAGE_SIZE - (ofs % PAGE_SIZE);
+		if (size < chunk)
+			chunk = size;
+		bdl[2] = cpu_to_le32(chunk);
+		/* program the IOC to enable interrupt
+		 * only when the whole fragment is processed
+		 */
+		size -= chunk;
+		bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+		bdl += 4;
+		azx_dev->frags++;
+		ofs += chunk;
+	}
+	*bdlp = bdl;
+	return ofs;
+}
+
+/*
+ * set up BDL entries
+ */
+static int azx_setup_periods(struct azx *chip,
+			     struct snd_pcm_substream *substream,
+			     struct azx_dev *azx_dev)
+{
 	u32 *bdl;
 	int i, ofs, periods, period_bytes;
+	int pos_adj;
 
 	/* reset BDL address */
 	azx_sd_writel(azx_dev, SD_BDLPL, 0);
 	azx_sd_writel(azx_dev, SD_BDLPU, 0);
 
 	period_bytes = snd_pcm_lib_period_bytes(substream);
+	azx_dev->period_bytes = period_bytes;
 	periods = azx_dev->bufsize / period_bytes;
 
 	/* program the initial BDL entries */
 	bdl = (u32 *)azx_dev->bdl.area;
 	ofs = 0;
 	azx_dev->frags = 0;
-	for (i = 0; i < periods; i++) {
-		int size, rest;
-		if (i >= AZX_MAX_BDL_ENTRIES) {
-			snd_printk(KERN_ERR "Too many BDL entries: "
-				   "buffer=%d, period=%d\n",
-				   azx_dev->bufsize, period_bytes);
-			/* reset */
-			azx_sd_writel(azx_dev, SD_BDLPL, 0);
-			azx_sd_writel(azx_dev, SD_BDLPU, 0);
-			return -EINVAL;
+	azx_dev->irq_ignore = 0;
+	pos_adj = bdl_pos_adj[chip->dev_index];
+	if (pos_adj > 0) {
+		struct snd_pcm_runtime *runtime = substream->runtime;
+		pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
+		if (!pos_adj)
+			pos_adj = 1;
+		pos_adj = frames_to_bytes(runtime, pos_adj);
+		if (pos_adj >= period_bytes) {
+			snd_printk(KERN_WARNING "Too big adjustment %d\n",
+				   bdl_pos_adj[chip->dev_index]);
+			pos_adj = 0;
+		} else {
+			ofs = setup_bdle(substream, azx_dev,
+					 &bdl, ofs, pos_adj, 1);
+			if (ofs < 0)
+				goto error;
+			azx_dev->irq_ignore = 1;
 		}
-		rest = period_bytes;
-		do {
-			dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
-			/* program the address field of the BDL entry */
-			bdl[0] = cpu_to_le32((u32)addr);
-			bdl[1] = cpu_to_le32(upper_32bit(addr));
-			/* program the size field of the BDL entry */
-			size = PAGE_SIZE - (ofs % PAGE_SIZE);
-			if (rest < size)
-				size = rest;
-			bdl[2] = cpu_to_le32(size);
-			/* program the IOC to enable interrupt
-			 * only when the whole fragment is processed
-			 */
-			rest -= size;
-			bdl[3] = rest ? 0 : cpu_to_le32(0x01);
-			bdl += 4;
-			azx_dev->frags++;
-			ofs += size;
-		} while (rest > 0);
+	} else
+		pos_adj = 0;
+	for (i = 0; i < periods; i++) {
+		if (i == periods - 1 && pos_adj)
+			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+					 period_bytes - pos_adj, 0);
+		else
+			ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+					 period_bytes, 1);
+		if (ofs < 0)
+			goto error;
 	}
 	return 0;
+
+ error:
+	snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
+		   azx_dev->bufsize, period_bytes);
+	/* reset */
+	azx_sd_writel(azx_dev, SD_BDLPL, 0);
+	azx_sd_writel(azx_dev, SD_BDLPU, 0);
+	return -EINVAL;
 }
 
 /*
@@ -1062,7 +1133,7 @@
 	/* lower BDL address */
 	azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
 	/* upper BDL address */
-	azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr));
+	azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
 
 	/* enable the position buffer */
 	if (chip->position_fix == POS_FIX_POSBUF ||
@@ -1085,7 +1156,7 @@
  */
 
 static unsigned int azx_max_codecs[] __devinitdata = {
-	[AZX_DRIVER_ICH] = 3,
+	[AZX_DRIVER_ICH] = 4,		/* Some ICH9 boards use SD3 */
 	[AZX_DRIVER_SCH] = 3,
 	[AZX_DRIVER_ATI] = 4,
 	[AZX_DRIVER_ATIHDMI] = 4,
@@ -1093,6 +1164,7 @@
 	[AZX_DRIVER_SIS] = 3,		/* FIXME: correct? */
 	[AZX_DRIVER_ULI] = 3,		/* FIXME: correct? */
 	[AZX_DRIVER_NVIDIA] = 3,	/* FIXME: correct? */
+	[AZX_DRIVER_TERA] = 1,
 };
 
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
@@ -1316,7 +1388,7 @@
 
 	snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
 		    azx_dev->bufsize, azx_dev->format_val);
-	if (azx_setup_periods(substream, azx_dev) < 0)
+	if (azx_setup_periods(chip, substream, azx_dev) < 0)
 		return -EINVAL;
 	azx_setup_controller(chip, azx_dev);
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -1421,35 +1493,113 @@
 	return 0;
 }
 
-static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+static unsigned int azx_get_position(struct azx *chip,
+				     struct azx_dev *azx_dev)
 {
-	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-	struct azx *chip = apcm->chip;
-	struct azx_dev *azx_dev = get_azx_dev(substream);
 	unsigned int pos;
 
 	if (chip->position_fix == POS_FIX_POSBUF ||
 	    chip->position_fix == POS_FIX_AUTO) {
 		/* use the position buffer */
 		pos = le32_to_cpu(*azx_dev->posbuf);
-		if (chip->position_fix == POS_FIX_AUTO &&
-		    azx_dev->period_intr == 1 && !pos) {
-			printk(KERN_WARNING
-			       "hda-intel: Invalid position buffer, "
-			       "using LPIB read method instead.\n");
-			chip->position_fix = POS_FIX_NONE;
-			goto read_lpib;
-		}
 	} else {
-	read_lpib:
 		/* read LPIB */
 		pos = azx_sd_readl(azx_dev, SD_LPIB);
-		if (chip->position_fix == POS_FIX_FIFO)
-			pos += azx_dev->fifo_size;
 	}
 	if (pos >= azx_dev->bufsize)
 		pos = 0;
-	return bytes_to_frames(substream->runtime, pos);
+	return pos;
+}
+
+static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+	struct azx *chip = apcm->chip;
+	struct azx_dev *azx_dev = get_azx_dev(substream);
+	return bytes_to_frames(substream->runtime,
+			       azx_get_position(chip, azx_dev));
+}
+
+/*
+ * Check whether the current DMA position is acceptable for updating
+ * periods.  Returns non-zero if it's OK.
+ *
+ * Many HD-audio controllers appear pretty inaccurate about
+ * the update-IRQ timing.  The IRQ is issued before actually the
+ * data is processed.  So, we need to process it afterwords in a
+ * workqueue.
+ */
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
+{
+	unsigned int pos;
+
+	pos = azx_get_position(chip, azx_dev);
+	if (chip->position_fix == POS_FIX_AUTO) {
+		if (!pos) {
+			printk(KERN_WARNING
+			       "hda-intel: Invalid position buffer, "
+			       "using LPIB read method instead.\n");
+			chip->position_fix = POS_FIX_LPIB;
+			pos = azx_get_position(chip, azx_dev);
+		} else
+			chip->position_fix = POS_FIX_POSBUF;
+	}
+
+	if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
+		return 0; /* NG - it's below the period boundary */
+	return 1; /* OK, it's fine */
+}
+
+/*
+ * The work for pending PCM period updates.
+ */
+static void azx_irq_pending_work(struct work_struct *work)
+{
+	struct azx *chip = container_of(work, struct azx, irq_pending_work);
+	int i, pending;
+
+	if (!chip->irq_pending_warned) {
+		printk(KERN_WARNING
+		       "hda-intel: IRQ timing workaround is activated "
+		       "for card #%d. Suggest a bigger bdl_pos_adj.\n",
+		       chip->card->number);
+		chip->irq_pending_warned = 1;
+	}
+
+	for (;;) {
+		pending = 0;
+		spin_lock_irq(&chip->reg_lock);
+		for (i = 0; i < chip->num_streams; i++) {
+			struct azx_dev *azx_dev = &chip->azx_dev[i];
+			if (!azx_dev->irq_pending ||
+			    !azx_dev->substream ||
+			    !azx_dev->running)
+				continue;
+			if (azx_position_ok(chip, azx_dev)) {
+				azx_dev->irq_pending = 0;
+				spin_unlock(&chip->reg_lock);
+				snd_pcm_period_elapsed(azx_dev->substream);
+				spin_lock(&chip->reg_lock);
+			} else
+				pending++;
+		}
+		spin_unlock_irq(&chip->reg_lock);
+		if (!pending)
+			return;
+		cond_resched();
+	}
+}
+
+/* clear irq_pending flags and assure no on-going workq */
+static void azx_clear_irq_pending(struct azx *chip)
+{
+	int i;
+
+	spin_lock_irq(&chip->reg_lock);
+	for (i = 0; i < chip->num_streams; i++)
+		chip->azx_dev[i].irq_pending = 0;
+	spin_unlock_irq(&chip->reg_lock);
+	flush_scheduled_work();
 }
 
 static struct snd_pcm_ops azx_pcm_ops = {
@@ -1676,6 +1826,7 @@
 	int i;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	azx_clear_irq_pending(chip);
 	for (i = 0; i < AZX_MAX_PCMS; i++)
 		snd_pcm_suspend_all(chip->pcm[i]);
 	if (chip->initialized)
@@ -1732,6 +1883,7 @@
 	int i;
 
 	if (chip->initialized) {
+		azx_clear_irq_pending(chip);
 		for (i = 0; i < chip->num_streams; i++)
 			azx_stream_stop(chip, &chip->azx_dev[i]);
 		azx_stop_chip(chip);
@@ -1770,9 +1922,9 @@
  * white/black-listing for position_fix
  */
 static struct snd_pci_quirk position_fix_list[] __devinitdata = {
-	SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE),
-	SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE),
-	SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE),
+	SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
+	SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+	SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
 	{}
 };
 
@@ -1857,12 +2009,25 @@
 	chip->irq = -1;
 	chip->driver_type = driver_type;
 	chip->msi = enable_msi;
+	chip->dev_index = dev;
+	INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
 
 	chip->position_fix = check_position_fix(chip, position_fix[dev]);
 	check_probe_mask(chip, dev);
 
 	chip->single_cmd = single_cmd;
 
+	if (bdl_pos_adj[dev] < 0) {
+		switch (chip->driver_type) {
+		case AZX_DRIVER_ICH:
+			bdl_pos_adj[dev] = 1;
+			break;
+		default:
+			bdl_pos_adj[dev] = 32;
+			break;
+		}
+	}
+
 #if BITS_PER_LONG != 64
 	/* Fix up base address on ULI M5461 */
 	if (chip->driver_type == AZX_DRIVER_ULI) {
@@ -2089,6 +2254,7 @@
 	{ PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
+	{ PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
 	{ PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
@@ -2141,6 +2307,8 @@
 	{ PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
+	/* Teradici */
+	{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 5633f77..1e5aff5 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -366,8 +366,6 @@
 {
 	unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
 						AC_VERB_GET_DIGI_CONVERT_1, 0);
-	unsigned int digi2 = snd_hda_codec_read(codec, nid, 0,
-						AC_VERB_GET_DIGI_CONVERT_2, 0);
 	snd_iprintf(buffer, "  Digital:");
 	if (digi1 & AC_DIG1_ENABLE)
 		snd_iprintf(buffer, " Enabled");
@@ -386,7 +384,8 @@
 	if (digi1 & AC_DIG1_LEVEL)
 		snd_iprintf(buffer, " GenLevel");
 	snd_iprintf(buffer, "\n");
-	snd_iprintf(buffer, "  Digital category: 0x%x\n", digi2 & AC_DIG2_CC);
+	snd_iprintf(buffer, "  Digital category: 0x%x\n",
+		    (digi1 >> 8) & AC_DIG2_CC);
 }
 
 static const char *get_pwr_state(u32 state)
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index a99e86d..e8003d9 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -23,7 +23,6 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
-#include <linux/mutex.h>
 
 #include <sound/core.h>
 #include "hda_codec.h"
@@ -64,7 +63,6 @@
 	/* PCM information */
 	struct hda_pcm pcm_rec[3];	/* used in alc_build_pcms() */
 
-	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
 	unsigned int spdif_route;
 
 	/* dynamic controls, init_verbs and input_mux */
@@ -1618,6 +1616,7 @@
 
 static struct snd_pci_quirk ad1981_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
+	SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
 	/* All HP models */
 	SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
@@ -2623,7 +2622,7 @@
 {
 	struct ad198x_spec *spec = codec->spec;
 	hda_nid_t nid;
-	int idx, err;
+	int i, idx, err;
 	char name[32];
 
 	if (! pin)
@@ -2631,16 +2630,26 @@
 
 	idx = ad1988_pin_idx(pin);
 	nid = ad1988_idx_to_dac(codec, idx);
-	/* specify the DAC as the extra output */
-	if (! spec->multiout.hp_nid)
-		spec->multiout.hp_nid = nid;
-	else
-		spec->multiout.extra_out_nid[0] = nid;
-	/* control HP volume/switch on the output mixer amp */
-	sprintf(name, "%s Playback Volume", pfx);
-	if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
-			       HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
-		return err;
+	/* check whether the corresponding DAC was already taken */
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		hda_nid_t pin = spec->autocfg.line_out_pins[i];
+		hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
+		if (dac == nid)
+			break;
+	}
+	if (i >= spec->autocfg.line_outs) {
+		/* specify the DAC as the extra output */
+		if (!spec->multiout.hp_nid)
+			spec->multiout.hp_nid = nid;
+		else
+			spec->multiout.extra_out_nid[0] = nid;
+		/* control HP volume/switch on the output mixer amp */
+		sprintf(name, "%s Playback Volume", pfx);
+		err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+		if (err < 0)
+			return err;
+	}
 	nid = ad1988_mixer_nids[idx];
 	sprintf(name, "%s Playback Switch", pfx);
 	if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
@@ -3177,7 +3186,6 @@
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -3847,7 +3855,6 @@
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -4152,7 +4159,6 @@
 	if (spec == NULL)
 		return -ENOMEM;
 
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 6;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 36fd852..7c1eb23 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -82,7 +82,6 @@
 	/* PCM information */
 	struct hda_pcm pcm_rec[2];	/* used in build_pcms() */
 
-	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
 	unsigned int spdif_route;
 
 	/* dynamic controls, init_verbs and input_mux */
@@ -687,7 +686,7 @@
 
 static struct hda_verb cxt5045_init_verbs[] = {
 	/* Line in, Mic */
-	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
 	/* HP, Amp  */
 	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
@@ -907,10 +906,12 @@
 	SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
 	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
 	SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
-	SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE),
+	SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505",
+		      CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
 	SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
@@ -928,7 +929,6 @@
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -963,6 +963,7 @@
 		codec->patch_ops.init = cxt5045_init;
 		break;
 	case CXT5045_LAPTOP_MICSENSE:
+		codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
 		spec->input_mux = &cxt5045_capture_source;
 		spec->num_init_verbs = 2;
 		spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
@@ -1007,15 +1008,19 @@
 #endif	
 	}
 
-	/*
-	 * Fix max PCM level to 0 dB
-	 * (originall it has 0x2b steps with 0dB offset 0x14)
-	 */
-	snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
-				  (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
-				  (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-				  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-				  (1 << AC_AMPCAP_MUTE_SHIFT));
+	switch (codec->subsystem_id >> 16) {
+	case 0x103c:
+		/* HP laptop has a really bad sound over 0dB on NID 0x17.
+		 * Fix max PCM level to 0 dB
+		 * (originall it has 0x2b steps with 0dB offset 0x14)
+		 */
+		snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+					  (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+					  (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+					  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+					  (1 << AC_AMPCAP_MUTE_SHIFT));
+		break;
+	}
 
 	return 0;
 }
@@ -1477,7 +1482,6 @@
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	spec->multiout.max_channels = 2;
@@ -1736,7 +1740,6 @@
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
-	mutex_init(&spec->amp_mutex);
 	codec->spec = spec;
 
 	codec->patch_ops = conexant_patch_ops;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index b0a2a26..2807bc8 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -163,6 +163,10 @@
 	ALC662_LENOVO_101E,
 	ALC662_ASUS_EEEPC_P701,
 	ALC662_ASUS_EEEPC_EP20,
+	ALC663_ASUS_M51VA,
+	ALC663_ASUS_G71V,
+	ALC663_ASUS_H13,
+	ALC663_ASUS_G50V,
 	ALC662_AUTO,
 	ALC662_MODEL_LAST,
 };
@@ -205,6 +209,7 @@
 	ALC883_MITAC,
 	ALC883_CLEVO_M720,
 	ALC883_FUJITSU_PI2515,
+	ALC883_3ST_6ch_INTEL,
 	ALC883_AUTO,
 	ALC883_MODEL_LAST,
 };
@@ -280,6 +285,10 @@
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	struct hda_loopback_check loopback;
 #endif
+
+	/* for PLL fix */
+	hda_nid_t pll_nid;
+	unsigned int pll_coef_idx, pll_coef_bit;
 };
 
 /*
@@ -747,6 +756,38 @@
 	{ }
 };
 
+/*
+ * Fix hardware PLL issue
+ * On some codecs, the analog PLL gating control must be off while
+ * the default value is 1.
+ */
+static void alc_fix_pll(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	unsigned int val;
+
+	if (!spec->pll_nid)
+		return;
+	snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
+			    spec->pll_coef_idx);
+	val = snd_hda_codec_read(codec, spec->pll_nid, 0,
+				 AC_VERB_GET_PROC_COEF, 0);
+	snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
+			    spec->pll_coef_idx);
+	snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF,
+			    val & ~(1 << spec->pll_coef_bit));
+}
+
+static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
+			     unsigned int coef_idx, unsigned int coef_bit)
+{
+	struct alc_spec *spec = codec->spec;
+	spec->pll_nid = nid;
+	spec->pll_coef_idx = coef_idx;
+	spec->pll_coef_bit = coef_bit;
+	alc_fix_pll(codec);
+}
+
 static void alc_sku_automute(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -776,6 +817,24 @@
 	alc_sku_automute(codec);
 }
 
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
+{
+	unsigned int tmp;
+
+	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0);
+	tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+	if ((tmp & 0xf0) == 2)
+		/* alc888S-VC */
+		snd_hda_codec_read(codec, 0x20, 0,
+				   AC_VERB_SET_PROC_COEF, 0x830);
+	 else
+		 /* alc888-VB */
+		 snd_hda_codec_read(codec, 0x20, 0,
+				    AC_VERB_SET_PROC_COEF, 0x3030);
+}
+
 /* 32-bit subsystem ID for BIOS loading in HD Audio codec.
  *	31 ~ 16 :	Manufacture ID
  *	15 ~ 8	:	SKU ID
@@ -851,8 +910,10 @@
 		case 0x10ec0267:
 		case 0x10ec0268:
 		case 0x10ec0269:
+		case 0x10ec0660:
+		case 0x10ec0662:
+		case 0x10ec0663:
 		case 0x10ec0862:
-		case 0x10ec0662:	
 		case 0x10ec0889:
 			snd_hda_codec_write(codec, 0x14, 0,
 					    AC_VERB_SET_EAPD_BTLENABLE, 2);
@@ -877,7 +938,6 @@
 		case 0x10ec0882:
 		case 0x10ec0883:
 		case 0x10ec0885:
-		case 0x10ec0888:
 		case 0x10ec0889:
 			snd_hda_codec_write(codec, 0x20, 0,
 					    AC_VERB_SET_COEF_INDEX, 7);
@@ -889,6 +949,9 @@
 					    AC_VERB_SET_PROC_COEF,
 					    tmp | 0x2010);
 			break;
+		case 0x10ec0888:
+			alc888_coef_init(codec);
+			break;
 		case 0x10ec0267:
 		case 0x10ec0268:
 			snd_hda_codec_write(codec, 0x20, 0,
@@ -2373,6 +2436,8 @@
 	struct alc_spec *spec = codec->spec;
 	unsigned int i;
 
+	alc_fix_pll(codec);
+
 	for (i = 0; i < spec->num_init_verbs; i++)
 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
 
@@ -3009,6 +3074,7 @@
 	SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
 	SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
 	SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+	SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU),
 	SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
 	SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
 	SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
@@ -5101,7 +5167,7 @@
 	SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
-	SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP),
+	SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
 	SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
@@ -6127,6 +6193,7 @@
 	SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+	SND_PCI_QUIRK(0x106b, 0x00a0, "Apple iMac 24''", ALC885_IMAC24),
 	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
 	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
@@ -6353,7 +6420,9 @@
 			continue;
 		vref = PIN_IN;
 		if (1 /*i <= AUTO_PIN_FRONT_MIC*/) {
-			if (snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) &
+			unsigned int pincap;
+			pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+			if ((pincap >> AC_PINCAP_VREF_SHIFT) &
 			    AC_PINCAP_VREF_80)
 				vref = PIN_VREF80;
 		}
@@ -6450,8 +6519,9 @@
 		case 0x106b1000: /* iMac 24 */
 			board_config = ALC885_IMAC24;
 			break;
-		case 0x106b00a1: /* Macbook */
+		case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
 		case 0x106b2c00: /* Macbook Pro rev3 */
+		case 0x106b3600: /* Macbook 3.1 */
 			board_config = ALC885_MBP3;
 			break;
 		default:
@@ -6485,14 +6555,20 @@
 	if (board_config != ALC882_AUTO)
 		setup_preset(spec, &alc882_presets[board_config]);
 
-	spec->stream_name_analog = "ALC882 Analog";
+	if (codec->vendor_id == 0x10ec0885) {
+		spec->stream_name_analog = "ALC885 Analog";
+		spec->stream_name_digital = "ALC885 Digital";
+	} else {
+		spec->stream_name_analog = "ALC882 Analog";
+		spec->stream_name_digital = "ALC882 Digital";
+	}
+
 	spec->stream_analog_playback = &alc882_pcm_analog_playback;
 	spec->stream_analog_capture = &alc882_pcm_analog_capture;
 	/* FIXME: setup DAC5 */
 	/*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
 	spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
 
-	spec->stream_name_digital = "ALC882 Digital";
 	spec->stream_digital_playback = &alc882_pcm_digital_playback;
 	spec->stream_digital_capture = &alc882_pcm_digital_capture;
 
@@ -6569,6 +6645,16 @@
 	},
 };
 
+static struct hda_input_mux alc883_3stack_6ch_intel = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x1 },
+		{ "Front Mic", 0x0 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
 static struct hda_input_mux alc883_lenovo_101e_capture_source = {
 	.num_items = 2,
 	.items = {
@@ -6650,6 +6736,48 @@
 };
 
 /*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
+	{ 2, alc883_3ST_ch2_intel_init },
+	{ 4, alc883_3ST_ch4_intel_init },
+	{ 6, alc883_3ST_ch6_intel_init },
+};
+
+/*
  * 6ch mode
  */
 static struct hda_verb alc883_sixstack_ch6_init[] = {
@@ -6881,15 +7009,54 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+			      HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc883_mux_enum_info,
+		.get = alc883_mux_enum_get,
+		.put = alc883_mux_enum_put,
+	},
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
 	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
 	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
@@ -7729,6 +7896,7 @@
 	[ALC883_MITAC]		= "mitac",
 	[ALC883_CLEVO_M720]	= "clevo-m720",
 	[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
+	[ALC883_3ST_6ch_INTEL]	= "3stack-6ch-intel",
 	[ALC883_AUTO]		= "auto",
 };
 
@@ -7786,6 +7954,8 @@
 	SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
 	SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
+	SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
+	SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
 	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
 	{}
 };
@@ -7824,6 +7994,18 @@
 		.need_dac_fix = 1,
 		.input_mux = &alc883_capture_source,
 	},
+	[ALC883_3ST_6ch_INTEL] = {
+		.mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes),
+		.channel_mode = alc883_3ST_6ch_intel_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_3stack_6ch_intel,
+	},
 	[ALC883_6ST_DIG] = {
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
 		.init_verbs = { alc883_init_verbs },
@@ -8145,6 +8327,8 @@
 
 	codec->spec = spec;
 
+	alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+
 	board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
 						  alc883_models,
 						  alc883_cfg_tbl);
@@ -8171,12 +8355,25 @@
 	if (board_config != ALC883_AUTO)
 		setup_preset(spec, &alc883_presets[board_config]);
 
-	spec->stream_name_analog = "ALC883 Analog";
+	switch (codec->vendor_id) {
+	case 0x10ec0888:
+		spec->stream_name_analog = "ALC888 Analog";
+		spec->stream_name_digital = "ALC888 Digital";
+		break;
+	case 0x10ec0889:
+		spec->stream_name_analog = "ALC889 Analog";
+		spec->stream_name_digital = "ALC889 Digital";
+		break;
+	default:
+		spec->stream_name_analog = "ALC883 Analog";
+		spec->stream_name_digital = "ALC883 Digital";
+		break;
+	}
+
 	spec->stream_analog_playback = &alc883_pcm_analog_playback;
 	spec->stream_analog_capture = &alc883_pcm_analog_capture;
 	spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
 
-	spec->stream_name_digital = "ALC883 Digital";
 	spec->stream_digital_playback = &alc883_pcm_digital_playback;
 	spec->stream_digital_capture = &alc883_pcm_digital_capture;
 
@@ -8189,6 +8386,9 @@
 	codec->patch_ops = alc_patch_ops;
 	if (board_config == ALC883_AUTO)
 		spec->init_hook = alc883_auto_init;
+	else if (codec->vendor_id == 0x10ec0888)
+		spec->init_hook = alc888_coef_init;
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc883_loopbacks;
@@ -9522,6 +9722,8 @@
 	SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
+		      ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
 	SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
 	SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
@@ -9729,6 +9931,8 @@
 	}
 #endif
 
+	alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+
 	board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
 						  alc262_models,
 						  alc262_cfg_tbl);
@@ -10674,12 +10878,18 @@
 	if (board_config != ALC268_AUTO)
 		setup_preset(spec, &alc268_presets[board_config]);
 
-	spec->stream_name_analog = "ALC268 Analog";
+	if (codec->vendor_id == 0x10ec0267) {
+		spec->stream_name_analog = "ALC267 Analog";
+		spec->stream_name_digital = "ALC267 Digital";
+	} else {
+		spec->stream_name_analog = "ALC268 Analog";
+		spec->stream_name_digital = "ALC268 Digital";
+	}
+
 	spec->stream_analog_playback = &alc268_pcm_analog_playback;
 	spec->stream_analog_capture = &alc268_pcm_analog_capture;
 	spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
 
-	spec->stream_name_digital = "ALC268 Digital";
 	spec->stream_digital_playback = &alc268_pcm_digital_playback;
 
 	if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
@@ -11033,6 +11243,8 @@
 
 	codec->spec = spec;
 
+	alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
 	board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
 						  alc269_models,
 						  alc269_cfg_tbl);
@@ -12631,6 +12843,12 @@
 	{ }
 };
 
+static struct hda_verb alc660vd_eapd_verbs[] = {
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{ }
+};
+
 static struct hda_verb alc861vd_lenovo_unsol_verbs[] = {
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -12786,6 +13004,7 @@
 	SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
 	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
+	SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 N200", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
 	{}
 };
@@ -13168,11 +13387,19 @@
 	if (board_config != ALC861VD_AUTO)
 		setup_preset(spec, &alc861vd_presets[board_config]);
 
-	spec->stream_name_analog = "ALC861VD Analog";
+	if (codec->vendor_id == 0x10ec0660) {
+		spec->stream_name_analog = "ALC660-VD Analog";
+		spec->stream_name_digital = "ALC660-VD Digital";
+		/* always turn on EAPD */
+		spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs;
+	} else {
+		spec->stream_name_analog = "ALC861VD Analog";
+		spec->stream_name_digital = "ALC861VD Digital";
+	}
+
 	spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
 	spec->stream_analog_capture = &alc861vd_pcm_analog_capture;
 
-	spec->stream_name_digital = "ALC861VD Digital";
 	spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
 	spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
 
@@ -13251,6 +13478,23 @@
 	},
 };
 
+static struct hda_input_mux alc663_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+	},
+};
+
+static struct hda_input_mux alc663_m51va_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Ext-Mic", 0x0 },
+		{ "D-Mic", 0x9 },
+	},
+};
+
 #define alc662_mux_enum_info alc_mux_enum_info
 #define alc662_mux_enum_get alc_mux_enum_get
 #define alc662_mux_enum_put alc882_mux_enum_put
@@ -13431,6 +13675,44 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc663_m51va_mixer[] = {
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("DMic Playback Switch", 0x23, 0x9, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc663_g71v_mixer[] = {
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc663_g50v_mixer[] = {
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc662_chmode_mixer[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -13501,6 +13783,11 @@
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+	/* always trun on EAPD */
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+
 	{ }
 };
 
@@ -13571,6 +13858,43 @@
 	{ }
 };
 
+static struct hda_verb alc663_m51va_init_verbs[] = {
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Headphone */
+
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
+
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc663_g71v_init_verbs[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
+	/* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */
+
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Headphone */
+
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_MIC_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc663_g50v_init_verbs[] = {
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Headphone */
+
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
 /* capture mixer elements */
 static struct snd_kcontrol_new alc662_capture_mixer[] = {
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
@@ -13692,6 +14016,125 @@
 	alc662_eeepc_ep20_automute(codec);
 }
 
+static void alc663_m51va_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x21, 0,
+				     AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
+	bits = present ? HDA_AMP_MUTE : 0;
+	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc663_m51va_mic_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x18, 0,
+				     AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
+	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
+	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
+	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
+}
+
+static void alc663_m51va_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_m51va_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc663_m51va_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_m51va_inithook(struct hda_codec *codec)
+{
+	alc663_m51va_speaker_automute(codec);
+	alc663_m51va_mic_automute(codec);
+}
+
+static void alc663_g71v_hp_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x21, 0,
+				     AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
+	bits = present ? HDA_AMP_MUTE : 0;
+	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc663_g71v_front_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	present = snd_hda_codec_read(codec, 0x15, 0,
+				     AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
+	bits = present ? HDA_AMP_MUTE : 0;
+	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc663_g71v_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_g71v_hp_automute(codec);
+		break;
+	case ALC880_FRONT_EVENT:
+		alc663_g71v_front_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_g71v_inithook(struct hda_codec *codec)
+{
+	alc663_g71v_front_automute(codec);
+	alc663_g71v_hp_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+
+static void alc663_g50v_unsol_event(struct hda_codec *codec,
+					   unsigned int res)
+{
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
+		alc663_m51va_speaker_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc662_eeepc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc663_g50v_inithook(struct hda_codec *codec)
+{
+	alc663_m51va_speaker_automute(codec);
+	alc662_eeepc_mic_automute(codec);
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc662_loopbacks	alc880_loopbacks
 #endif
@@ -13714,14 +14157,24 @@
 	[ALC662_LENOVO_101E]	= "lenovo-101e",
 	[ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
 	[ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
+	[ALC663_ASUS_M51VA] = "m51va",
+	[ALC663_ASUS_G71V] = "g71v",
+	[ALC663_ASUS_H13] = "h13",
+	[ALC663_ASUS_G50V] = "g50v",
 	[ALC662_AUTO]		= "auto",
 };
 
 static struct snd_pci_quirk alc662_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS G71V", ALC663_ASUS_G71V),
+	SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),
+	SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS M51VA", ALC663_ASUS_G50V),
 	SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
 	SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
 	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
+	SND_PCI_QUIRK(0x1854, 0x2000, "ASUS H13-2000", ALC663_ASUS_H13),
+	SND_PCI_QUIRK(0x1854, 0x2001, "ASUS H13-2001", ALC663_ASUS_H13),
+	SND_PCI_QUIRK(0x1854, 0x2002, "ASUS H13-2002", ALC663_ASUS_H13),
 	{}
 };
 
@@ -13809,7 +14262,53 @@
 		.unsol_event = alc662_eeepc_ep20_unsol_event,
 		.init_hook = alc662_eeepc_ep20_inithook,
 	},
-
+	[ALC663_ASUS_M51VA] = {
+		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc663_m51va_capture_source,
+		.unsol_event = alc663_m51va_unsol_event,
+		.init_hook = alc663_m51va_inithook,
+	},
+	[ALC663_ASUS_G71V] = {
+		.mixers = { alc663_g71v_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc662_eeepc_capture_source,
+		.unsol_event = alc663_g71v_unsol_event,
+		.init_hook = alc663_g71v_inithook,
+	},
+	[ALC663_ASUS_H13] = {
+		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.dac_nids = alc662_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc663_m51va_capture_source,
+		.unsol_event = alc663_m51va_unsol_event,
+		.init_hook = alc663_m51va_inithook,
+	},
+	[ALC663_ASUS_G50V] = {
+		.mixers = { alc663_g50v_mixer, alc662_capture_mixer},
+		.init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
+		.dac_nids = alc662_dac_nids,
+		.dig_out_nid = ALC662_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
+		.channel_mode = alc662_3ST_6ch_modes,
+		.input_mux = &alc663_capture_source,
+		.unsol_event = alc663_g50v_unsol_event,
+		.init_hook = alc663_g50v_inithook,
+	},
 };
 
 
@@ -14082,6 +14581,8 @@
 
 	codec->spec = spec;
 
+	alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
 	board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
 						  alc662_models,
 			  	                  alc662_cfg_tbl);
@@ -14108,11 +14609,17 @@
 	if (board_config != ALC662_AUTO)
 		setup_preset(spec, &alc662_presets[board_config]);
 
-	spec->stream_name_analog = "ALC662 Analog";
+	if (codec->vendor_id == 0x10ec0663) {
+		spec->stream_name_analog = "ALC663 Analog";
+		spec->stream_name_digital = "ALC663 Digital";
+	} else {
+		spec->stream_name_analog = "ALC662 Analog";
+		spec->stream_name_digital = "ALC662 Digital";
+	}
+
 	spec->stream_analog_playback = &alc662_pcm_analog_playback;
 	spec->stream_analog_capture = &alc662_pcm_analog_capture;
 
-	spec->stream_name_digital = "ALC662 Digital";
 	spec->stream_digital_playback = &alc662_pcm_digital_playback;
 	spec->stream_digital_capture = &alc662_pcm_digital_capture;
 
@@ -14151,6 +14658,7 @@
 	  .patch = patch_alc883 },
 	{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
 	  .patch = patch_alc662 },
+	{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
 	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index a4f44a0..08cb77f 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -636,21 +636,28 @@
 	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 };
 
+#define HD_DISABLE_PORTF 3
 static struct hda_verb stac92hd71bxx_analog_core_init[] = {
+	/* start of config #1 */
+
+	/* connect port 0f to audio mixer */
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+	/* unmute right and left channels for node 0x0f */
+	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	/* start of config #2 */
+
 	/* set master volume and direct control */
 	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
 	/* connect headphone jack to dac1 */
 	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	/* connect ports 0d and 0f to audio mixer */
+	/* connect port 0d to audio mixer */
 	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
-	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
 	/* unmute dac0 input in audio mixer */
 	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
-	/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
+	/* unmute right and left channels for nodes 0x0a, 0xd */
 	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{}
 };
 
@@ -818,6 +825,9 @@
 	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
 
+	HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT),
+
 	HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
 	HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
 	{ } /* end */
@@ -1317,13 +1327,13 @@
 	0x90a000f0, 0x01452050,
 };
 
-static unsigned int dell_m4_1_pin_configs[13] = {
+static unsigned int dell_m4_1_pin_configs[10] = {
 	0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
 	0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
 	0x40f000f0, 0x4f0000f0,
 };
 
-static unsigned int dell_m4_2_pin_configs[13] = {
+static unsigned int dell_m4_2_pin_configs[10] = {
 	0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
 	0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
 	0x40f000f0, 0x044413b0,
@@ -1754,12 +1764,8 @@
 		      "unknown Dell", STAC_9205_DELL_M42),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
 		      "Dell Precision", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
-			  "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
 		      "Dell Precision", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
-		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
 		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
@@ -1770,18 +1776,14 @@
 		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
 		      "Dell Precision M4300", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
-		      "Dell Precision", STAC_9205_DELL_M43),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
-	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
-		      "Dell Inspiron", STAC_9205_DELL_M44),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
 		      "unknown Dell", STAC_9205_DELL_M42),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
+		      "Dell Precision", STAC_9205_DELL_M43),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
+		      "Dell Precision", STAC_9205_DELL_M43),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
+		      "Dell Precision", STAC_9205_DELL_M43),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
 		      "Dell Inspiron", STAC_9205_DELL_M44),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
@@ -3103,13 +3105,16 @@
 					0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i],
 					0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+		def_conf = get_defcfg_connect(def_conf);
 		/* outputs are only ports capable of power management
 		 * any attempts on powering down a input port cause the
 		 * referenced VREF to act quirky.
 		 */
 		if (pinctl & AC_PINCTL_IN_EN)
 			continue;
-		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED)
+		/* skip any ports that don't have jacks since presence
+ 		 * detection is useless */
+		if (def_conf && def_conf != AC_JACK_PORT_FIXED)
 			continue;
 		enable_pin_detect(codec, spec->pwr_nids[i], event | i);
 		codec->patch_ops.unsol_event(codec, (event | i) << 26);
@@ -3614,6 +3619,7 @@
 
 	codec->spec = spec;
 	spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+	spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
 	spec->pin_nids = stac92hd71bxx_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec,
 							STAC_92HD71BXX_MODELS,
@@ -3642,6 +3648,19 @@
 		spec->mixer = stac92hd71bxx_mixer;
 		spec->init = stac92hd71bxx_core_init;
 		break;
+	case 0x111d7608: /* 5 Port with Analog Mixer */
+		/* no output amps */
+		spec->num_pwrs = 0;
+		spec->mixer = stac92hd71bxx_analog_mixer;
+
+		/* disable VSW */
+		spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
+		stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
+		break;
+	case 0x111d7603: /* 6 Port with Analog Mixer */
+		/* no output amps */
+		spec->num_pwrs = 0;
+		/* fallthru */
 	default:
 		spec->mixer = stac92hd71bxx_analog_mixer;
 		spec->init = stac92hd71bxx_analog_core_init;
@@ -3653,22 +3672,19 @@
 	/* GPIO0 High = EAPD */
 	spec->gpio_mask = 0x01;
 	spec->gpio_dir = 0x01;
-	spec->gpio_mask = 0x01;
 	spec->gpio_data = 0x01;
 
 	spec->mux_nids = stac92hd71bxx_mux_nids;
 	spec->adc_nids = stac92hd71bxx_adc_nids;
 	spec->dmic_nids = stac92hd71bxx_dmic_nids;
 	spec->dmux_nids = stac92hd71bxx_dmux_nids;
+	spec->pwr_nids = stac92hd71bxx_pwr_nids;
 
 	spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
 	spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
 	spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
 	spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
 
-	spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
-	spec->pwr_nids = stac92hd71bxx_pwr_nids;
-
 	spec->multiout.num_dacs = 1;
 	spec->multiout.hp_nid = 0x11;
 	spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
@@ -4306,10 +4322,11 @@
  	{ .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
  	{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
  	{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
+	{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
+	{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
 	{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
-	{ .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx },
 	{ .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
 	{ .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
 	{ .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
diff --git a/sound/pci/ice1712/envy24ht.h b/sound/pci/ice1712/envy24ht.h
index 43b9e3e..a0c5e00 100644
--- a/sound/pci/ice1712/envy24ht.h
+++ b/sound/pci/ice1712/envy24ht.h
@@ -93,9 +93,13 @@
 #define VT1724_REG_MPU_TXFIFO		0x0a	/*byte ro. number of bytes in TX fifo*/
 #define VT1724_REG_MPU_RXFIFO		0x0b	/*byte ro. number of bytes in RX fifo*/
 
-//are these 2 the wrong way around? they don't seem to be used yet anyway
-#define VT1724_REG_MPU_CTRL		0x0c	/* byte */
-#define VT1724_REG_MPU_DATA		0x0d	/* byte */
+#define VT1724_REG_MPU_DATA		0x0c	/* byte */
+#define VT1724_REG_MPU_CTRL		0x0d	/* byte */
+#define   VT1724_MPU_UART	0x01
+#define   VT1724_MPU_TX_EMPTY	0x02
+#define   VT1724_MPU_TX_FULL	0x04
+#define   VT1724_MPU_RX_EMPTY	0x08
+#define   VT1724_MPU_RX_FULL	0x10
 
 #define VT1724_REG_MPU_FIFO_WM	0x0e	/*byte set the high/low watermarks for RX/TX fifos*/
 #define   VT1724_MPU_RX_FIFO	0x20	//1=rx fifo watermark 0=tx fifo watermark
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 3208901..762fbd7 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -333,6 +333,8 @@
 	unsigned int has_spdif: 1;	/* VT1720/4 - has SPDIF I/O */
 	unsigned int force_pdma4: 1;	/* VT1720/4 - PDMA4 as non-spdif */
 	unsigned int force_rdma1: 1;	/* VT1720/4 - RDMA1 as non-spdif */
+	unsigned int midi_output: 1;	/* VT1720/4: MIDI output triggered */
+	unsigned int midi_input: 1;	/* VT1720/4: MIDI input triggered */
 	unsigned int num_total_dacs;	/* total DACs */
 	unsigned int num_total_adcs;	/* total ADCs */
 	unsigned int cur_rate;		/* current rate */
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 6735090..e596d77 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -32,7 +32,7 @@
 #include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/info.h>
-#include <sound/mpu401.h>
+#include <sound/rawmidi.h>
 #include <sound/initval.h>
 
 #include <sound/asoundef.h>
@@ -223,30 +223,153 @@
 }
 
 /*
- * MPU401 accessor
+ * MIDI
  */
-static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu,
-					    unsigned long addr)
+
+static void vt1724_midi_clear_rx(struct snd_ice1712 *ice)
 {
-	/* fix status bits to the standard position */
-	/* only RX_EMPTY and TX_FULL are checked */
-	if (addr == MPU401C(mpu))
-		return (inb(addr) & 0x0c) << 4;
-	else
-		return inb(addr);
+	unsigned int count;
+
+	for (count = inb(ICEREG1724(ice, MPU_RXFIFO)); count > 0; --count)
+		inb(ICEREG1724(ice, MPU_DATA));
 }
 
-static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu,
-				    unsigned char data, unsigned long addr)
+static inline struct snd_rawmidi_substream *
+get_rawmidi_substream(struct snd_ice1712 *ice, unsigned int stream)
 {
-	if (addr == MPU401C(mpu)) {
-		if (data == MPU401_ENTER_UART)
-			outb(0x01, addr);
-		/* what else? */
-	} else
-		outb(data, addr);
+	return list_first_entry(&ice->rmidi[0]->streams[stream].substreams,
+				struct snd_rawmidi_substream, list);
 }
 
+static void vt1724_midi_write(struct snd_ice1712 *ice)
+{
+	struct snd_rawmidi_substream *s;
+	int count, i;
+	u8 buffer[32];
+
+	s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_OUTPUT);
+	count = 31 - inb(ICEREG1724(ice, MPU_TXFIFO));
+	if (count > 0) {
+		count = snd_rawmidi_transmit(s, buffer, count);
+		for (i = 0; i < count; ++i)
+			outb(buffer[i], ICEREG1724(ice, MPU_DATA));
+	}
+}
+
+static void vt1724_midi_read(struct snd_ice1712 *ice)
+{
+	struct snd_rawmidi_substream *s;
+	int count, i;
+	u8 buffer[32];
+
+	s = get_rawmidi_substream(ice, SNDRV_RAWMIDI_STREAM_INPUT);
+	count = inb(ICEREG1724(ice, MPU_RXFIFO));
+	if (count > 0) {
+		count = min(count, 32);
+		for (i = 0; i < count; ++i)
+			buffer[i] = inb(ICEREG1724(ice, MPU_DATA));
+		snd_rawmidi_receive(s, buffer, count);
+	}
+}
+
+static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
+				   u8 flag, int enable)
+{
+	struct snd_ice1712 *ice = substream->rmidi->private_data;
+	u8 mask;
+
+	spin_lock_irq(&ice->reg_lock);
+	mask = inb(ICEREG1724(ice, IRQMASK));
+	if (enable)
+		mask &= ~flag;
+	else
+		mask |= flag;
+	outb(mask, ICEREG1724(ice, IRQMASK));
+	spin_unlock_irq(&ice->reg_lock);
+}
+
+static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
+{
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 1);
+	return 0;
+}
+
+static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
+{
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_TX, 0);
+	return 0;
+}
+
+static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
+{
+	struct snd_ice1712 *ice = s->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	if (up) {
+		ice->midi_output = 1;
+		vt1724_midi_write(ice);
+	} else {
+		ice->midi_output = 0;
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
+{
+	struct snd_ice1712 *ice = s->rmidi->private_data;
+	unsigned long timeout;
+
+	/* 32 bytes should be transmitted in less than about 12 ms */
+	timeout = jiffies + msecs_to_jiffies(15);
+	do {
+		if (inb(ICEREG1724(ice, MPU_CTRL)) & VT1724_MPU_TX_EMPTY)
+			break;
+		schedule_timeout_uninterruptible(1);
+	} while (time_after(timeout, jiffies));
+}
+
+static struct snd_rawmidi_ops vt1724_midi_output_ops = {
+	.open = vt1724_midi_output_open,
+	.close = vt1724_midi_output_close,
+	.trigger = vt1724_midi_output_trigger,
+	.drain = vt1724_midi_output_drain,
+};
+
+static int vt1724_midi_input_open(struct snd_rawmidi_substream *s)
+{
+	vt1724_midi_clear_rx(s->rmidi->private_data);
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 1);
+	return 0;
+}
+
+static int vt1724_midi_input_close(struct snd_rawmidi_substream *s)
+{
+	vt1724_enable_midi_irq(s, VT1724_IRQ_MPU_RX, 0);
+	return 0;
+}
+
+static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
+{
+	struct snd_ice1712 *ice = s->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	if (up) {
+		ice->midi_input = 1;
+		vt1724_midi_read(ice);
+	} else {
+		ice->midi_input = 0;
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+}
+
+static struct snd_rawmidi_ops vt1724_midi_input_ops = {
+	.open = vt1724_midi_input_open,
+	.close = vt1724_midi_input_close,
+	.trigger = vt1724_midi_input_trigger,
+};
+
 
 /*
  *  Interrupt handler
@@ -278,13 +401,10 @@
 #endif
 		handled = 1;		
 		if (status & VT1724_IRQ_MPU_TX) {
-			if (ice->rmidi[0])
-				snd_mpu401_uart_interrupt_tx(irq,
-					ice->rmidi[0]->private_data);
-			else /* disable TX to be sure */
-				outb(inb(ICEREG1724(ice, IRQMASK)) |
-				     VT1724_IRQ_MPU_TX,
-				     ICEREG1724(ice, IRQMASK));
+			spin_lock(&ice->reg_lock);
+			if (ice->midi_output)
+				vt1724_midi_write(ice);
+			spin_unlock(&ice->reg_lock);
 			/* Due to mysterical reasons, MPU_TX is always
 			 * generated (and can't be cleared) when a PCM
 			 * playback is going.  So let's ignore at the
@@ -293,13 +413,12 @@
 			status_mask &= ~VT1724_IRQ_MPU_TX;
 		}
 		if (status & VT1724_IRQ_MPU_RX) {
-			if (ice->rmidi[0])
-				snd_mpu401_uart_interrupt(irq,
-					ice->rmidi[0]->private_data);
-			else /* disable RX to be sure */
-				outb(inb(ICEREG1724(ice, IRQMASK)) |
-				     VT1724_IRQ_MPU_RX,
-				     ICEREG1724(ice, IRQMASK));
+			spin_lock(&ice->reg_lock);
+			if (ice->midi_input)
+				vt1724_midi_read(ice);
+			else
+				vt1724_midi_clear_rx(ice);
+			spin_unlock(&ice->reg_lock);
 		}
 		/* ack MPU irq */
 		outb(status, ICEREG1724(ice, IRQSTAT));
@@ -2425,28 +2544,30 @@
 
 	if (! c->no_mpu401) {
 		if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
-			struct snd_mpu401 *mpu;
-			if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
-						       ICEREG1724(ice, MPU_CTRL),
-						       (MPU401_INFO_INTEGRATED |
-							MPU401_INFO_NO_ACK |
-							MPU401_INFO_TX_IRQ),
-						       ice->irq, 0,
-						       &ice->rmidi[0])) < 0) {
+			struct snd_rawmidi *rmidi;
+
+			err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
+			if (err < 0) {
 				snd_card_free(card);
 				return err;
 			}
-			mpu = ice->rmidi[0]->private_data;
-			mpu->read = snd_vt1724_mpu401_read;
-			mpu->write = snd_vt1724_mpu401_write;
-			/* unmask MPU RX/TX irqs */
-			outb(inb(ICEREG1724(ice, IRQMASK)) &
-			     ~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
-			     ICEREG1724(ice, IRQMASK));
+			ice->rmidi[0] = rmidi;
+			rmidi->private_data = ice;
+			strcpy(rmidi->name, "ICE1724 MIDI");
+			rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+					    SNDRV_RAWMIDI_INFO_INPUT |
+					    SNDRV_RAWMIDI_INFO_DUPLEX;
+			snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+					    &vt1724_midi_output_ops);
+			snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+					    &vt1724_midi_input_ops);
+
 			/* set watermarks */
 			outb(VT1724_MPU_RX_FIFO | 0x1,
 			     ICEREG1724(ice, MPU_FIFO_WM));
 			outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
+			/* set UART mode */
+			outb(VT1724_MPU_UART, ICEREG1724(ice, MPU_CTRL));
 		}
 	}
 
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index a536c59..f4788de 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2427,6 +2427,29 @@
 	outw(0xffff, io + GPIO_MASK);
 }
 
+static void
+snd_m3_hv_init(struct snd_m3 *chip)
+{
+	unsigned long io = chip->iobase;
+	u16 val = GPI_VOL_DOWN | GPI_VOL_UP;
+
+	if (!chip->is_omnibook)
+		return;
+
+	/*
+	 * Volume buttons on some HP OmniBook laptops
+	 * require some GPIO magic to work correctly.
+	 */
+	outw(0xffff, io + GPIO_MASK);
+	outw(0x0000, io + GPIO_DATA);
+
+	outw(~val, io + GPIO_MASK);
+	outw(inw(io + GPIO_DIRECTION) & ~val, io + GPIO_DIRECTION);
+	outw(val, io + GPIO_MASK);
+
+	outw(0xffff, io + GPIO_MASK);
+}
+
 static int
 snd_m3_chip_init(struct snd_m3 *chip)
 {
@@ -2442,21 +2465,6 @@
 	       DISABLE_LEGACY);
 	pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
 
-	if (chip->is_omnibook) {
-		/*
-		 * Volume buttons on some HP OmniBook laptops don't work
-		 * correctly. This makes them work for the most part.
-		 *
-		 * Volume up and down buttons on the laptop side work.
-		 * Fn+cursor_up (volme up) works.
-		 * Fn+cursor_down (volume down) doesn't work.
-		 * Fn+F7 (mute) works acts as volume up.
-		 */
-		outw(~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_MASK);
-		outw(inw(io + GPIO_DIRECTION) & ~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DIRECTION);
-		outw((GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DATA);
-		outw(0xffff, io + GPIO_MASK);
-	}
 	pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
 	n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD);
 	n |= chip->hv_config;
@@ -2642,6 +2650,8 @@
 	snd_m3_enable_ints(chip);
 	snd_m3_amp_enable(chip, 1);
 
+	snd_m3_hv_init(chip);
+
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
 }
@@ -2781,6 +2791,8 @@
 
 	snd_m3_amp_enable(chip, 1);
 
+	snd_m3_hv_init(chip);
+
 	tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
 
 	if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 7efb838..06d13e7 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -1302,8 +1302,8 @@
 		.read = snd_nm256_ac97_read,
 	};
 
-	chip->ac97_regs = kcalloc(sizeof(short),
-				  ARRAY_SIZE(nm256_ac97_init_val), GFP_KERNEL);
+	chip->ac97_regs = kcalloc(ARRAY_SIZE(nm256_ac97_init_val),
+				  sizeof(short), GFP_KERNEL);
 	if (! chip->ac97_regs)
 		return -ENOMEM;
 
diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c
index 090dd43..7442460 100644
--- a/sound/pci/oxygen/hifier.c
+++ b/sound/pci/oxygen/hifier.c
@@ -28,7 +28,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("TempoTec HiFier driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -62,16 +62,28 @@
 			 AK4396_WRITE | (reg << 8) | value);
 }
 
+static void update_ak4396_volume(struct oxygen *chip)
+{
+	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
+}
+
+static void hifier_registers_init(struct oxygen *chip)
+{
+	struct hifier_data *data = chip->model_data;
+
+	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+	ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
+	ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
+	update_ak4396_volume(chip);
+}
+
 static void hifier_init(struct oxygen *chip)
 {
 	struct hifier_data *data = chip->model_data;
 
 	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
-	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
-	ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
-	ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
-	ak4396_write(chip, AK4396_LCH_ATT, 0);
-	ak4396_write(chip, AK4396_RCH_ATT, 0);
+	hifier_registers_init(chip);
 
 	snd_component_add(chip->card, "AK4396");
 	snd_component_add(chip->card, "CS5340");
@@ -100,12 +112,6 @@
 	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
 }
 
-static void update_ak4396_volume(struct oxygen *chip)
-{
-	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
-	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
-}
-
 static void update_ak4396_mute(struct oxygen *chip)
 {
 	struct hifier_data *data = chip->model_data;
@@ -140,6 +146,7 @@
 	.init = hifier_init,
 	.control_filter = hifier_control_filter,
 	.cleanup = hifier_cleanup,
+	.resume = hifier_registers_init,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_cs5340_params,
 	.update_dac_volume = update_ak4396_volume,
@@ -180,6 +187,10 @@
 	.id_table = hifier_ids,
 	.probe = hifier_probe,
 	.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+	.suspend = oxygen_pci_suspend,
+	.resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_hifier_init(void)
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 63f185c..7c8ae31 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -43,7 +43,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("C-Media CMI8788 driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -80,6 +80,7 @@
 
 struct generic_data {
 	u8 ak4396_ctl2;
+	u16 saved_wm8785_registers[2];
 };
 
 static void ak4396_write(struct oxygen *chip, unsigned int codec,
@@ -99,20 +100,35 @@
 
 static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
 {
+	struct generic_data *data = chip->model_data;
+
 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 			 OXYGEN_SPI_DATA_LENGTH_2 |
 			 OXYGEN_SPI_CLOCK_160 |
 			 (3 << OXYGEN_SPI_CODEC_SHIFT) |
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
 			 (reg << 9) | value);
+	if (reg < ARRAY_SIZE(data->saved_wm8785_registers))
+		data->saved_wm8785_registers[reg] = value;
 }
 
-static void ak4396_init(struct oxygen *chip)
+static void update_ak4396_volume(struct oxygen *chip)
+{
+	unsigned int i;
+
+	for (i = 0; i < 4; ++i) {
+		ak4396_write(chip, i,
+			     AK4396_LCH_ATT, chip->dac_volume[i * 2]);
+		ak4396_write(chip, i,
+			     AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
+	}
+}
+
+static void ak4396_registers_init(struct oxygen *chip)
 {
 	struct generic_data *data = chip->model_data;
 	unsigned int i;
 
-	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
 	for (i = 0; i < 4; ++i) {
 		ak4396_write(chip, i,
 			     AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
@@ -120,9 +136,16 @@
 			     AK4396_CONTROL_2, data->ak4396_ctl2);
 		ak4396_write(chip, i,
 			     AK4396_CONTROL_3, AK4396_PCM);
-		ak4396_write(chip, i, AK4396_LCH_ATT, 0);
-		ak4396_write(chip, i, AK4396_RCH_ATT, 0);
 	}
+	update_ak4396_volume(chip);
+}
+
+static void ak4396_init(struct oxygen *chip)
+{
+	struct generic_data *data = chip->model_data;
+
+	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+	ak4396_registers_init(chip);
 	snd_component_add(chip->card, "AK4396");
 }
 
@@ -133,12 +156,23 @@
 	snd_component_add(chip->card, "AK5385");
 }
 
+static void wm8785_registers_init(struct oxygen *chip)
+{
+	struct generic_data *data = chip->model_data;
+
+	wm8785_write(chip, WM8785_R7, 0);
+	wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]);
+	wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]);
+}
+
 static void wm8785_init(struct oxygen *chip)
 {
-	wm8785_write(chip, WM8785_R7, 0);
-	wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE |
-		     WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST);
-	wm8785_write(chip, WM8785_R1, WM8785_WL_24);
+	struct generic_data *data = chip->model_data;
+
+	data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE |
+		WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
+	data->saved_wm8785_registers[1] = WM8785_WL_24;
+	wm8785_registers_init(chip);
 	snd_component_add(chip->card, "WM8785");
 }
 
@@ -158,6 +192,12 @@
 {
 }
 
+static void generic_resume(struct oxygen *chip)
+{
+	ak4396_registers_init(chip);
+	wm8785_registers_init(chip);
+}
+
 static void set_ak4396_params(struct oxygen *chip,
 			      struct snd_pcm_hw_params *params)
 {
@@ -183,18 +223,6 @@
 	}
 }
 
-static void update_ak4396_volume(struct oxygen *chip)
-{
-	unsigned int i;
-
-	for (i = 0; i < 4; ++i) {
-		ak4396_write(chip, i,
-			     AK4396_LCH_ATT, chip->dac_volume[i * 2]);
-		ak4396_write(chip, i,
-			     AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
-	}
-}
-
 static void update_ak4396_mute(struct oxygen *chip)
 {
 	struct generic_data *data = chip->model_data;
@@ -256,6 +284,7 @@
 	.owner = THIS_MODULE,
 	.init = generic_init,
 	.cleanup = generic_cleanup,
+	.resume = generic_resume,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_wm8785_params,
 	.update_dac_volume = update_ak4396_volume,
@@ -283,6 +312,7 @@
 	.owner = THIS_MODULE,
 	.init = meridian_init,
 	.cleanup = generic_cleanup,
+	.resume = ak4396_registers_init,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_ak5385_params,
 	.update_dac_volume = update_ak4396_volume,
@@ -331,6 +361,10 @@
 	.id_table = oxygen_ids,
 	.probe = generic_oxygen_probe,
 	.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+	.suspend = oxygen_pci_suspend,
+	.resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_oxygen_init(void)
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index a71c6e0..74a6448 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -16,6 +16,8 @@
 #define PCM_AC97	5
 #define PCM_COUNT	6
 
+#define OXYGEN_IO_SIZE	0x100
+
 /* model-specific configuration of outputs/inputs */
 #define PLAYBACK_0_TO_I2S	0x001
 #define PLAYBACK_1_TO_SPDIF	0x004
@@ -78,6 +80,12 @@
 	struct work_struct spdif_input_bits_work;
 	struct work_struct gpio_work;
 	wait_queue_head_t ac97_waitqueue;
+	union {
+		u8 _8[OXYGEN_IO_SIZE];
+		__le16 _16[OXYGEN_IO_SIZE / 2];
+		__le32 _32[OXYGEN_IO_SIZE / 4];
+	} saved_registers;
+	u16 saved_ac97_registers[2][0x40];
 };
 
 struct oxygen_model {
@@ -89,6 +97,8 @@
 	int (*control_filter)(struct snd_kcontrol_new *template);
 	int (*mixer_init)(struct oxygen *chip);
 	void (*cleanup)(struct oxygen *chip);
+	void (*suspend)(struct oxygen *chip);
+	void (*resume)(struct oxygen *chip);
 	void (*pcm_hardware_filter)(unsigned int channel,
 				    struct snd_pcm_hardware *hardware);
 	void (*set_dac_params)(struct oxygen *chip,
@@ -117,6 +127,10 @@
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
 		     const struct oxygen_model *model);
 void oxygen_pci_remove(struct pci_dev *pci);
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
+int oxygen_pci_resume(struct pci_dev *pci);
+#endif
 
 /* oxygen_mixer.c */
 
diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c
index 5569606..83f135f 100644
--- a/sound/pci/oxygen/oxygen_io.c
+++ b/sound/pci/oxygen/oxygen_io.c
@@ -44,18 +44,21 @@
 void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
 {
 	outb(value, chip->addr + reg);
+	chip->saved_registers._8[reg] = value;
 }
 EXPORT_SYMBOL(oxygen_write8);
 
 void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
 {
 	outw(value, chip->addr + reg);
+	chip->saved_registers._16[reg / 2] = cpu_to_le16(value);
 }
 EXPORT_SYMBOL(oxygen_write16);
 
 void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
 {
 	outl(value, chip->addr + reg);
+	chip->saved_registers._32[reg / 4] = cpu_to_le32(value);
 }
 EXPORT_SYMBOL(oxygen_write32);
 
@@ -63,7 +66,10 @@
 			  u8 value, u8 mask)
 {
 	u8 tmp = inb(chip->addr + reg);
-	outb((tmp & ~mask) | (value & mask), chip->addr + reg);
+	tmp &= ~mask;
+	tmp |= value & mask;
+	outb(tmp, chip->addr + reg);
+	chip->saved_registers._8[reg] = tmp;
 }
 EXPORT_SYMBOL(oxygen_write8_masked);
 
@@ -71,7 +77,10 @@
 			   u16 value, u16 mask)
 {
 	u16 tmp = inw(chip->addr + reg);
-	outw((tmp & ~mask) | (value & mask), chip->addr + reg);
+	tmp &= ~mask;
+	tmp |= value & mask;
+	outw(tmp, chip->addr + reg);
+	chip->saved_registers._16[reg / 2] = cpu_to_le16(tmp);
 }
 EXPORT_SYMBOL(oxygen_write16_masked);
 
@@ -79,7 +88,10 @@
 			   u32 value, u32 mask)
 {
 	u32 tmp = inl(chip->addr + reg);
-	outl((tmp & ~mask) | (value & mask), chip->addr + reg);
+	tmp &= ~mask;
+	tmp |= value & mask;
+	outl(tmp, chip->addr + reg);
+	chip->saved_registers._32[reg / 4] = cpu_to_le32(tmp);
 }
 EXPORT_SYMBOL(oxygen_write32_masked);
 
@@ -128,8 +140,10 @@
 		oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
 		/* require two "completed" writes, just to be sure */
 		if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 &&
-		    ++succeeded >= 2)
+		    ++succeeded >= 2) {
+			chip->saved_ac97_registers[codec][index / 2] = data;
 			return;
+		}
 	}
 	snd_printk(KERN_ERR "AC'97 write timeout\n");
 }
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index 897697d..22f3785 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -32,7 +32,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("C-Media CMI8788 helper library");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 
 
 static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
@@ -173,7 +173,7 @@
 	int i, j;
 
 	snd_iprintf(buffer, "CMI8788\n\n");
-	for (i = 0; i < 0x100; i += 0x10) {
+	for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
 		snd_iprintf(buffer, "%02x:", i);
 		for (j = 0; j < 0x10; ++j)
 			snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
@@ -314,6 +314,10 @@
 				    OXYGEN_SPDIF_LOCK_MASK |
 				    OXYGEN_SPDIF_RATE_MASK);
 	oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_STANDARD);
 	oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
 	oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
 	oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
@@ -455,7 +459,7 @@
 	}
 
 	if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
-	    pci_resource_len(pci, 0) < 0x100) {
+	    pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) {
 		snd_printk(KERN_ERR "invalid PCI I/O range\n");
 		err = -ENXIO;
 		goto err_pci_regions;
@@ -534,3 +538,99 @@
 	pci_set_drvdata(pci, NULL);
 }
 EXPORT_SYMBOL(oxygen_pci_remove);
+
+#ifdef CONFIG_PM
+int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct oxygen *chip = card->private_data;
+	unsigned int i, saved_interrupt_mask;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+	for (i = 0; i < PCM_COUNT; ++i)
+		if (chip->streams[i])
+			snd_pcm_suspend(chip->streams[i]);
+
+	if (chip->model->suspend)
+		chip->model->suspend(chip);
+
+	spin_lock_irq(&chip->reg_lock);
+	saved_interrupt_mask = chip->interrupt_mask;
+	chip->interrupt_mask = 0;
+	oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+	spin_unlock_irq(&chip->reg_lock);
+
+	synchronize_irq(chip->irq);
+	flush_scheduled_work();
+	chip->interrupt_mask = saved_interrupt_mask;
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+	return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_suspend);
+
+static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = {
+	0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff,
+	0x00300000, 0x00000fe4, 0x0ff7001f, 0x00000000
+};
+static const u32 ac97_registers_to_restore[2][0x40 / 32] = {
+	{ 0x18284fa2, 0x03060000 },
+	{ 0x00007fa6, 0x00200000 }
+};
+
+static inline int is_bit_set(const u32 *bitmap, unsigned int bit)
+{
+	return bitmap[bit / 32] & (1 << (bit & 31));
+}
+
+static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)
+{
+	unsigned int i;
+
+	oxygen_write_ac97(chip, codec, AC97_RESET, 0);
+	msleep(1);
+	for (i = 1; i < 0x40; ++i)
+		if (is_bit_set(ac97_registers_to_restore[codec], i))
+			oxygen_write_ac97(chip, codec, i * 2,
+					  chip->saved_ac97_registers[codec][i]);
+}
+
+int oxygen_pci_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct oxygen *chip = card->private_data;
+	unsigned int i;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+	if (pci_enable_device(pci) < 0) {
+		snd_printk(KERN_ERR "cannot reenable device");
+		snd_card_disconnect(card);
+		return -EIO;
+	}
+	pci_set_master(pci);
+
+	oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
+	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
+	for (i = 0; i < OXYGEN_IO_SIZE; ++i)
+		if (is_bit_set(registers_to_restore, i))
+			oxygen_write8(chip, i, chip->saved_registers._8[i]);
+	if (chip->has_ac97_0)
+		oxygen_restore_ac97(chip, 0);
+	if (chip->has_ac97_1)
+		oxygen_restore_ac97(chip, 1);
+
+	if (chip->model->resume)
+		chip->model->resume(chip);
+
+	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+EXPORT_SYMBOL(oxygen_pci_resume);
+#endif /* CONFIG_PM */
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index b17c405..c4ad65a 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -24,6 +24,16 @@
 #include <sound/pcm_params.h>
 #include "oxygen.h"
 
+/* most DMA channels have a 16-bit counter for 32-bit words */
+#define BUFFER_BYTES_MAX		((1 << 16) * 4)
+/* the multichannel DMA channel has a 24-bit counter */
+#define BUFFER_BYTES_MAX_MULTICH	((1 << 24) * 4)
+
+#define PERIOD_BYTES_MIN		64
+
+#define DEFAULT_BUFFER_BYTES		(BUFFER_BYTES_MAX / 2)
+#define DEFAULT_BUFFER_BYTES_MULTICH	(1024 * 1024)
+
 static const struct snd_pcm_hardware oxygen_stereo_hardware = {
 	.info = SNDRV_PCM_INFO_MMAP |
 		SNDRV_PCM_INFO_MMAP_VALID |
@@ -44,11 +54,11 @@
 	.rate_max = 192000,
 	.channels_min = 2,
 	.channels_max = 2,
-	.buffer_bytes_max = 256 * 1024,
-	.period_bytes_min = 128,
-	.period_bytes_max = 128 * 1024,
+	.buffer_bytes_max = BUFFER_BYTES_MAX,
+	.period_bytes_min = PERIOD_BYTES_MIN,
+	.period_bytes_max = BUFFER_BYTES_MAX / 2,
 	.periods_min = 2,
-	.periods_max = 2048,
+	.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
 };
 static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
 	.info = SNDRV_PCM_INFO_MMAP |
@@ -70,11 +80,11 @@
 	.rate_max = 192000,
 	.channels_min = 2,
 	.channels_max = 8,
-	.buffer_bytes_max = 2048 * 1024,
-	.period_bytes_min = 128,
-	.period_bytes_max = 256 * 1024,
+	.buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH,
+	.period_bytes_min = PERIOD_BYTES_MIN,
+	.period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2,
 	.periods_min = 2,
-	.periods_max = 16384,
+	.periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
 };
 static const struct snd_pcm_hardware oxygen_ac97_hardware = {
 	.info = SNDRV_PCM_INFO_MMAP |
@@ -88,11 +98,11 @@
 	.rate_max = 48000,
 	.channels_min = 2,
 	.channels_max = 2,
-	.buffer_bytes_max = 256 * 1024,
-	.period_bytes_min = 128,
-	.period_bytes_max = 128 * 1024,
+	.buffer_bytes_max = BUFFER_BYTES_MAX,
+	.period_bytes_min = PERIOD_BYTES_MIN,
+	.period_bytes_max = BUFFER_BYTES_MAX / 2,
 	.periods_min = 2,
-	.periods_max = 2048,
+	.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
 };
 
 static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
@@ -155,6 +165,12 @@
 		if (err < 0)
 			return err;
 	}
+	if (channel == PCM_MULTICH) {
+		err = snd_pcm_hw_constraint_minmax
+			(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 0, 8192000);
+		if (err < 0)
+			return err;
+	}
 	snd_pcm_set_sync(substream);
 	chip->streams[channel] = substream;
 
@@ -517,6 +533,7 @@
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
 		pausing = 0;
 		break;
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
@@ -663,12 +680,14 @@
 			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
 						      SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      512 * 1024, 2048 * 1024);
+						      DEFAULT_BUFFER_BYTES_MULTICH,
+						      BUFFER_BYTES_MAX_MULTICH);
 		if (ins)
 			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
 						      SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      128 * 1024, 256 * 1024);
+						      DEFAULT_BUFFER_BYTES,
+						      BUFFER_BYTES_MAX);
 	}
 
 	outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF);
@@ -688,7 +707,8 @@
 		strcpy(pcm->name, "Digital");
 		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      128 * 1024, 256 * 1024);
+						      DEFAULT_BUFFER_BYTES,
+						      BUFFER_BYTES_MAX);
 	}
 
 	if (chip->has_ac97_1) {
@@ -718,7 +738,8 @@
 		strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
 		snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						      snd_dma_pci_data(chip->pci),
-						      128 * 1024, 256 * 1024);
+						      DEFAULT_BUFFER_BYTES,
+						      BUFFER_BYTES_MAX);
 	}
 	return 0;
 }
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index 7f84fa5..9a2c16b 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -79,7 +79,7 @@
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("Asus AVx00 driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -132,6 +132,9 @@
 	u8 ext_power_int_reg;
 	u8 ext_power_bit;
 	u8 has_power;
+	u8 pcm1796_oversampling;
+	u8 cs4398_fm;
+	u8 cs4362a_fm;
 };
 
 static void pcm1796_write(struct oxygen *chip, unsigned int codec,
@@ -159,6 +162,14 @@
 	oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
 }
 
+static void xonar_enable_output(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	msleep(data->anti_pop_delay);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
 static void xonar_common_init(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -170,32 +181,59 @@
 		data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
 				     & data->ext_power_bit);
 	}
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_CS53x1_M_MASK | data->output_enable_bit);
 	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
 			      GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
 	oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
-	msleep(data->anti_pop_delay);
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
-	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+	xonar_enable_output(chip);
+}
+
+static void update_pcm1796_volume(struct oxygen *chip)
+{
+	unsigned int i;
+
+	for (i = 0; i < 4; ++i) {
+		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
+		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
+	}
+}
+
+static void update_pcm1796_mute(struct oxygen *chip)
+{
+	unsigned int i;
+	u8 value;
+
+	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+	if (chip->dac_mute)
+		value |= PCM1796_MUTE;
+	for (i = 0; i < 4; ++i)
+		pcm1796_write(chip, i, 18, value);
+}
+
+static void pcm1796_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+	unsigned int i;
+
+	for (i = 0; i < 4; ++i) {
+		pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
+		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
+		pcm1796_write(chip, i, 21, 0);
+	}
+	update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */
+	update_pcm1796_volume(chip);
 }
 
 static void xonar_d2_init(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
-	unsigned int i;
 
 	data->anti_pop_delay = 300;
 	data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
+	data->pcm1796_oversampling = PCM1796_OS_64;
 
-	for (i = 0; i < 4; ++i) {
-		pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED |
-			      PCM1796_FMT_24_LJUST | PCM1796_ATLD);
-		pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
-		pcm1796_write(chip, i, 20, PCM1796_OS_64);
-		pcm1796_write(chip, i, 21, 0);
-		pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */
-		pcm1796_write(chip, i, 17, 0x0f);
-	}
+	pcm1796_init(chip);
 
 	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
@@ -217,151 +255,6 @@
 	xonar_d2_init(chip);
 }
 
-static void xonar_dx_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->anti_pop_delay = 800;
-	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
-	data->ext_power_reg = OXYGEN_GPI_DATA;
-	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-	data->ext_power_bit = GPI_DX_EXT_POWER;
-
-	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
-		       OXYGEN_2WIRE_LENGTH_8 |
-		       OXYGEN_2WIRE_INTERRUPT_MASK |
-		       OXYGEN_2WIRE_SPEED_FAST);
-
-	/* set CPEN (control port mode) and power down */
-	cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
-	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
-	/* configure */
-	cs4398_write(chip, 2, CS4398_FM_SINGLE |
-		     CS4398_DEM_NONE | CS4398_DIF_LJUST);
-	cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
-	cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE);
-	cs4398_write(chip, 5, 0xfe);
-	cs4398_write(chip, 6, 0xfe);
-	cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
-		     CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
-	cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
-	cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
-		      CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
-	cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
-	cs4362a_write(chip, 0x05, 0);
-	cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE |
-		      CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
-	cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE |
-		      CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
-	cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE |
-		      CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L);
-	cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE);
-	cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE);
-	/* clear power down */
-	cs4398_write(chip, 8, CS4398_CPEN);
-	cs4362a_write(chip, 0x01, CS4362A_CPEN);
-
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
-			  GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
-			    GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
-
-	xonar_common_init(chip);
-
-	snd_component_add(chip->card, "CS4398");
-	snd_component_add(chip->card, "CS4362A");
-	snd_component_add(chip->card, "CS5361");
-}
-
-static void xonar_cleanup(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
-}
-
-static void xonar_dx_cleanup(struct oxygen *chip)
-{
-	xonar_cleanup(chip);
-	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
-	oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
-}
-
-static void set_pcm1796_params(struct oxygen *chip,
-			       struct snd_pcm_hw_params *params)
-{
-	unsigned int i;
-	u8 value;
-
-	value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
-	for (i = 0; i < 4; ++i)
-		pcm1796_write(chip, i, 20, value);
-}
-
-static void update_pcm1796_volume(struct oxygen *chip)
-{
-	unsigned int i;
-
-	for (i = 0; i < 4; ++i) {
-		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
-		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
-	}
-}
-
-static void update_pcm1796_mute(struct oxygen *chip)
-{
-	unsigned int i;
-	u8 value;
-
-	value = PCM1796_FMT_24_LJUST | PCM1796_ATLD;
-	if (chip->dac_mute)
-		value |= PCM1796_MUTE;
-	for (i = 0; i < 4; ++i)
-		pcm1796_write(chip, i, 18, value);
-}
-
-static void set_cs53x1_params(struct oxygen *chip,
-			      struct snd_pcm_hw_params *params)
-{
-	unsigned int value;
-
-	if (params_rate(params) <= 54000)
-		value = GPIO_CS53x1_M_SINGLE;
-	else if (params_rate(params) <= 108000)
-		value = GPIO_CS53x1_M_DOUBLE;
-	else
-		value = GPIO_CS53x1_M_QUAD;
-	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
-			      value, GPIO_CS53x1_M_MASK);
-}
-
-static void set_cs43xx_params(struct oxygen *chip,
-			      struct snd_pcm_hw_params *params)
-{
-	u8 fm_cs4398, fm_cs4362a;
-
-	fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST;
-	fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
-	if (params_rate(params) <= 50000) {
-		fm_cs4398 |= CS4398_FM_SINGLE;
-		fm_cs4362a |= CS4362A_FM_SINGLE;
-	} else if (params_rate(params) <= 100000) {
-		fm_cs4398 |= CS4398_FM_DOUBLE;
-		fm_cs4362a |= CS4362A_FM_DOUBLE;
-	} else {
-		fm_cs4398 |= CS4398_FM_QUAD;
-		fm_cs4362a |= CS4362A_FM_QUAD;
-	}
-	cs4398_write(chip, 2, fm_cs4398);
-	cs4362a_write(chip, 0x06, fm_cs4362a);
-	cs4362a_write(chip, 0x09, fm_cs4362a);
-	cs4362a_write(chip, 0x0c, fm_cs4362a);
-}
-
 static void update_cs4362a_volumes(struct oxygen *chip)
 {
 	u8 mute;
@@ -393,6 +286,141 @@
 	update_cs4362a_volumes(chip);
 }
 
+static void cs43xx_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	/* set CPEN (control port mode) and power down */
+	cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
+	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+	/* configure */
+	cs4398_write(chip, 2, data->cs4398_fm);
+	cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
+	cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
+		     CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
+	cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
+	cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
+		      CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
+	cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
+	cs4362a_write(chip, 0x05, 0);
+	cs4362a_write(chip, 0x06, data->cs4362a_fm);
+	cs4362a_write(chip, 0x09, data->cs4362a_fm);
+	cs4362a_write(chip, 0x0c, data->cs4362a_fm);
+	update_cs43xx_volume(chip);
+	update_cs43xx_mute(chip);
+	/* clear power down */
+	cs4398_write(chip, 8, CS4398_CPEN);
+	cs4362a_write(chip, 0x01, CS4362A_CPEN);
+}
+
+static void xonar_dx_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->anti_pop_delay = 800;
+	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
+	data->ext_power_reg = OXYGEN_GPI_DATA;
+	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+	data->ext_power_bit = GPI_DX_EXT_POWER;
+	data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
+	data->cs4362a_fm = CS4362A_FM_SINGLE |
+		CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_FAST);
+
+	cs43xx_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+			    GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
+
+	xonar_common_init(chip);
+
+	snd_component_add(chip->card, "CS4398");
+	snd_component_add(chip->card, "CS4362A");
+	snd_component_add(chip->card, "CS5361");
+}
+
+static void xonar_cleanup(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+static void xonar_dx_cleanup(struct oxygen *chip)
+{
+	xonar_cleanup(chip);
+	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+	oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
+}
+
+static void xonar_d2_resume(struct oxygen *chip)
+{
+	pcm1796_init(chip);
+	xonar_enable_output(chip);
+}
+
+static void xonar_dx_resume(struct oxygen *chip)
+{
+	cs43xx_init(chip);
+	xonar_enable_output(chip);
+}
+
+static void set_pcm1796_params(struct oxygen *chip,
+			       struct snd_pcm_hw_params *params)
+{
+	struct xonar_data *data = chip->model_data;
+	unsigned int i;
+
+	data->pcm1796_oversampling =
+		params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
+	for (i = 0; i < 4; ++i)
+		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
+}
+
+static void set_cs53x1_params(struct oxygen *chip,
+			      struct snd_pcm_hw_params *params)
+{
+	unsigned int value;
+
+	if (params_rate(params) <= 54000)
+		value = GPIO_CS53x1_M_SINGLE;
+	else if (params_rate(params) <= 108000)
+		value = GPIO_CS53x1_M_DOUBLE;
+	else
+		value = GPIO_CS53x1_M_QUAD;
+	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+			      value, GPIO_CS53x1_M_MASK);
+}
+
+static void set_cs43xx_params(struct oxygen *chip,
+			      struct snd_pcm_hw_params *params)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST;
+	data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+	if (params_rate(params) <= 50000) {
+		data->cs4398_fm |= CS4398_FM_SINGLE;
+		data->cs4362a_fm |= CS4362A_FM_SINGLE;
+	} else if (params_rate(params) <= 100000) {
+		data->cs4398_fm |= CS4398_FM_DOUBLE;
+		data->cs4362a_fm |= CS4362A_FM_DOUBLE;
+	} else {
+		data->cs4398_fm |= CS4398_FM_QUAD;
+		data->cs4362a_fm |= CS4362A_FM_QUAD;
+	}
+	cs4398_write(chip, 2, data->cs4398_fm);
+	cs4362a_write(chip, 0x06, data->cs4362a_fm);
+	cs4362a_write(chip, 0x09, data->cs4362a_fm);
+	cs4362a_write(chip, 0x0c, data->cs4362a_fm);
+}
+
 static void xonar_gpio_changed(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -535,6 +563,8 @@
 		.control_filter = xonar_d2_control_filter,
 		.mixer_init = xonar_mixer_init,
 		.cleanup = xonar_cleanup,
+		.suspend = xonar_cleanup,
+		.resume = xonar_d2_resume,
 		.set_dac_params = set_pcm1796_params,
 		.set_adc_params = set_cs53x1_params,
 		.update_dac_volume = update_pcm1796_volume,
@@ -563,6 +593,8 @@
 		.control_filter = xonar_d2_control_filter,
 		.mixer_init = xonar_mixer_init,
 		.cleanup = xonar_cleanup,
+		.suspend = xonar_cleanup,
+		.resume = xonar_d2_resume,
 		.set_dac_params = set_pcm1796_params,
 		.set_adc_params = set_cs53x1_params,
 		.update_dac_volume = update_pcm1796_volume,
@@ -592,6 +624,8 @@
 		.control_filter = xonar_dx_control_filter,
 		.mixer_init = xonar_dx_mixer_init,
 		.cleanup = xonar_dx_cleanup,
+		.suspend = xonar_dx_cleanup,
+		.resume = xonar_dx_resume,
 		.set_dac_params = set_cs43xx_params,
 		.set_adc_params = set_cs53x1_params,
 		.update_dac_volume = update_cs43xx_volume,
@@ -636,6 +670,10 @@
 	.id_table = xonar_ids,
 	.probe = xonar_probe,
 	.remove = __devexit_p(oxygen_pci_remove),
+#ifdef CONFIG_PM
+	.suspend = oxygen_pci_suspend,
+	.resume = oxygen_pci_resume,
+#endif
 };
 
 static int __init alsa_card_xonar_init(void)
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 7fdcdc8..2c7e253 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -516,7 +516,7 @@
 	int capture_mask = 0;
 	int playback_mask = 0;
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	struct timeval my_tv1, my_tv2;
 	do_gettimeofday(&my_tv1);
 #endif
@@ -623,7 +623,7 @@
 
 	mutex_unlock(&mgr->setup_mutex);
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	do_gettimeofday(&my_tv2);
 	snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n",
 		    (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 78aa81f..abe5c59 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -473,7 +473,7 @@
 [CMD_AUDIO_LEVEL_ADJUST] =		{ 0xc22000, 0, RMH_SSIZE_FIXED },
 };
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 static char* cmd_names[] = {
 [CMD_VERSION] =				"CMD_VERSION",
 [CMD_SUPPORTED] =			"CMD_SUPPORTED",
@@ -549,7 +549,7 @@
 				}
 			}
 		}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 		if (rmh->cmd_idx < CMD_LAST_INDEX)
 			snd_printdd("    stat[%d]=%x\n", i, data);
 #endif
@@ -597,7 +597,7 @@
 		data |= 0x008000;	/* MASK_MORE_THAN_1_WORD_COMMAND */
 	else
 		data &= 0xff7fff;	/* MASK_1_WORD_COMMAND */
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	if (rmh->cmd_idx < CMD_LAST_INDEX)
 		snd_printdd("MSG cmd[0]=%x (%s)\n", data, cmd_names[rmh->cmd_idx]);
 #endif
@@ -624,7 +624,7 @@
 		for (i=1; i < rmh->cmd_len; i++) {
 			/* send other words */
 			data = rmh->cmd[i];
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 			if (rmh->cmd_idx < CMD_LAST_INDEX)
 				snd_printdd("    cmd[%d]=%x\n", i, data);
 #endif
@@ -847,7 +847,7 @@
 	int state, i, err;
 	int audio_mask;
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	struct timeval my_tv1, my_tv2;
 	do_gettimeofday(&my_tv1);
 #endif
@@ -894,7 +894,7 @@
 		if (err)
 			return err;
 	}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	do_gettimeofday(&my_tv2);
 	snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n",
 		    (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
@@ -951,7 +951,7 @@
 				  enum pcxhr_async_err_src err_src, int pipe,
 				  int is_capture)
 {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	static char* err_src_name[] = {
 		[PCXHR_ERR_PIPE]	= "Pipe",
 		[PCXHR_ERR_STREAM]	= "Stream",
@@ -1169,7 +1169,7 @@
 				    mgr->dsp_time_last, dsp_time_new);
 			mgr->dsp_time_err++;
 		}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 		if (dsp_time_diff == 0)
 			snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new);
 		else if (dsp_time_diff >= (2*PCXHR_GRANULARITY))
@@ -1208,7 +1208,7 @@
 		mgr->src_it_dsp = reg;
 		tasklet_hi_schedule(&mgr->msg_taskq);
 	}
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 	if (reg & PCXHR_FATAL_DSP_ERR)
 		snd_printdd("FATAL DSP ERROR : %x\n", reg);
 #endif
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index bbcee2c..a69b420 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -1590,7 +1590,10 @@
 	if (spdif_flag) {
 		if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
 			outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS));
-			outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
+			val = trident->spdif_pcm_ctrl;
+			if (!go)
+				val &= ~(0x28);
+			outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3));
 		} else {
 			outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS));
 			val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN;
diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
index df9b487..3fd7f1b 100644
--- a/sound/pci/trident/trident_memory.c
+++ b/sound/pci/trident/trident_memory.c
@@ -310,181 +310,3 @@
 	mutex_unlock(&hdr->block_mutex);
 	return 0;
 }
-
-
-/*----------------------------------------------------------------
- * memory allocation using multiple pages (for synth)
- *----------------------------------------------------------------
- * Unlike the DMA allocation above, non-contiguous pages are
- * assigned to TLB.
- *----------------------------------------------------------------*/
-
-/*
- */
-static int synth_alloc_pages(struct snd_trident *hw, struct snd_util_memblk *blk);
-static int synth_free_pages(struct snd_trident *hw, struct snd_util_memblk *blk);
-
-/*
- * allocate a synth sample area
- */
-struct snd_util_memblk *
-snd_trident_synth_alloc(struct snd_trident *hw, unsigned int size)
-{
-	struct snd_util_memblk *blk;
-	struct snd_util_memhdr *hdr = hw->tlb.memhdr; 
-
-	mutex_lock(&hdr->block_mutex);
-	blk = __snd_util_mem_alloc(hdr, size);
-	if (blk == NULL) {
-		mutex_unlock(&hdr->block_mutex);
-		return NULL;
-	}
-	if (synth_alloc_pages(hw, blk)) {
-		__snd_util_mem_free(hdr, blk);
-		mutex_unlock(&hdr->block_mutex);
-		return NULL;
-	}
-	mutex_unlock(&hdr->block_mutex);
-	return blk;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_alloc);
-
-/*
- * free a synth sample area
- */
-int
-snd_trident_synth_free(struct snd_trident *hw, struct snd_util_memblk *blk)
-{
-	struct snd_util_memhdr *hdr = hw->tlb.memhdr; 
-
-	mutex_lock(&hdr->block_mutex);
-	synth_free_pages(hw, blk);
-	 __snd_util_mem_free(hdr, blk);
-	mutex_unlock(&hdr->block_mutex);
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_free);
-
-/*
- * reset TLB entry and free kernel page
- */
-static void clear_tlb(struct snd_trident *trident, int page)
-{
-	void *ptr = page_to_ptr(trident, page);
-	dma_addr_t addr = page_to_addr(trident, page);
-	set_silent_tlb(trident, page);
-	if (ptr) {
-		struct snd_dma_buffer dmab;
-		dmab.dev.type = SNDRV_DMA_TYPE_DEV;
-		dmab.dev.dev = snd_dma_pci_data(trident->pci);
-		dmab.area = ptr;
-		dmab.addr = addr;
-		dmab.bytes = ALIGN_PAGE_SIZE;
-		snd_dma_free_pages(&dmab);
-	}
-}
-
-/* check new allocation range */
-static void get_single_page_range(struct snd_util_memhdr *hdr,
-				  struct snd_util_memblk *blk,
-				  int *first_page_ret, int *last_page_ret)
-{
-	struct list_head *p;
-	struct snd_util_memblk *q;
-	int first_page, last_page;
-	first_page = firstpg(blk);
-	if ((p = blk->list.prev) != &hdr->block) {
-		q = list_entry(p, struct snd_util_memblk, list);
-		if (lastpg(q) == first_page)
-			first_page++;  /* first page was already allocated */
-	}
-	last_page = lastpg(blk);
-	if ((p = blk->list.next) != &hdr->block) {
-		q = list_entry(p, struct snd_util_memblk, list);
-		if (firstpg(q) == last_page)
-			last_page--; /* last page was already allocated */
-	}
-	*first_page_ret = first_page;
-	*last_page_ret = last_page;
-}
-
-/*
- * allocate kernel pages and assign them to TLB
- */
-static int synth_alloc_pages(struct snd_trident *hw, struct snd_util_memblk *blk)
-{
-	int page, first_page, last_page;
-	struct snd_dma_buffer dmab;
-
-	firstpg(blk) = get_aligned_page(blk->offset);
-	lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1);
-	get_single_page_range(hw->tlb.memhdr, blk, &first_page, &last_page);
-
-	/* allocate a kernel page for each Trident page -
-	 * fortunately Trident page size and kernel PAGE_SIZE is identical!
-	 */
-	for (page = first_page; page <= last_page; page++) {
-		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(hw->pci),
-					ALIGN_PAGE_SIZE, &dmab) < 0)
-			goto __fail;
-		if (! is_valid_page(dmab.addr)) {
-			snd_dma_free_pages(&dmab);
-			goto __fail;
-		}
-		set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr);
-	}
-	return 0;
-
-__fail:
-	/* release allocated pages */
-	last_page = page - 1;
-	for (page = first_page; page <= last_page; page++)
-		clear_tlb(hw, page);
-
-	return -ENOMEM;
-}
-
-/*
- * free pages
- */
-static int synth_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk)
-{
-	int page, first_page, last_page;
-
-	get_single_page_range(trident->tlb.memhdr, blk, &first_page, &last_page);
-	for (page = first_page; page <= last_page; page++)
-		clear_tlb(trident, page);
-
-	return 0;
-}
-
-/*
- * copy_from_user(blk + offset, data, size)
- */
-int snd_trident_synth_copy_from_user(struct snd_trident *trident,
-				     struct snd_util_memblk *blk,
-				     int offset, const char __user *data, int size)
-{
-	int page, nextofs, end_offset, temp, temp1;
-
-	offset += blk->offset;
-	end_offset = offset + size;
-	page = get_aligned_page(offset) + 1;
-	do {
-		nextofs = aligned_page_offset(page);
-		temp = nextofs - offset;
-		temp1 = end_offset - offset;
-		if (temp1 < temp)
-			temp = temp1;
-		if (copy_from_user(offset_ptr(trident, offset), data, temp))
-			return -EFAULT;
-		offset = nextofs;
-		data += temp;
-		page++;
-	} while (offset < end_offset);
-	return 0;
-}
-
-EXPORT_SYMBOL(snd_trident_synth_copy_from_user);
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index b585cc3..6781be9 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -1757,6 +1757,12 @@
 		.type = AC97_TUNE_HP_ONLY
 	},
 	{
+		.subvendor = 0x1019,
+		.subdevice = 0x1841,
+		.name = "ECS K7VTA3",
+		.type = AC97_TUNE_HP_ONLY
+	},
+	{
 		.subvendor = 0x1849,
 		.subdevice = 0x3059,
 		.name = "ASRock K7VM2",
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 29b3056..7129df5 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -2205,6 +2205,7 @@
 	for (reg = 0x80; reg < 0xc0; reg += 4)
 		snd_ymfpci_writel(chip, reg, 0);
 	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
+	snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0x3fff3fff);
 	snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff);
 	snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff);
 	snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
@@ -2324,6 +2325,7 @@
 		chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
 	chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
 	snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
+	snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
 	snd_ymfpci_disable_dsp(chip);
 	pci_disable_device(pci);
 	pci_save_state(pci);
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig
index c9fa1a2..7fbb190 100644
--- a/sound/pcmcia/Kconfig
+++ b/sound/pcmcia/Kconfig
@@ -1,11 +1,16 @@
 # ALSA PCMCIA drivers
 
-menu "PCMCIA devices"
-	depends on SND!=n && PCMCIA
+menuconfig SND_PCMCIA
+	bool "PCMCIA sound devices"
+	depends on PCMCIA
+	default y
+	help
+	  Support for sound devices connected via the PCMCIA bus.
+
+if SND_PCMCIA && PCMCIA
 
 config SND_VXPOCKET
 	tristate "Digigram VXpocket"
-	depends on SND && PCMCIA
 	select SND_VX_LIB
 	help
 	  Say Y here to include support for Digigram VXpocket and
@@ -16,7 +21,6 @@
 
 config SND_PDAUDIOCF
 	tristate "Sound Core PDAudioCF"
-	depends on SND && PCMCIA
 	select SND_PCM
 	help
 	  Say Y here to include support for Sound Core PDAudioCF
@@ -25,4 +29,5 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-pdaudiocf.
 
-endmenu
+endif	# SND_PCMCIA
+
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c
index 157b0b5..99bf2a6 100644
--- a/sound/pcmcia/vx/vxp_ops.c
+++ b/sound/pcmcia/vx/vxp_ops.c
@@ -151,7 +151,7 @@
 	unsigned int i;
 	int c;
 	int regCSUER, regRUER;
-	unsigned char *image;
+	const unsigned char *image;
 	unsigned char data;
 
 	/* Switch to programmation mode */
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig
index cacb0b1..777de2b 100644
--- a/sound/ppc/Kconfig
+++ b/sound/ppc/Kconfig
@@ -1,17 +1,17 @@
 # ALSA PowerMac drivers
 
-menu "ALSA PowerMac devices"
-	depends on SND!=n && PPC
+menuconfig SND_PPC
+	bool "PowerPC sound devices"
+	depends on PPC64 || PPC32
+	default y
+	help
+	  Support for sound devices specific to PowerPC architectures.
 
-comment "ALSA PowerMac requires I2C"
-	depends on SND && I2C=n
-
-comment "ALSA PowerMac requires INPUT"
-	depends on SND && INPUT=n
+if SND_PPC
 
 config SND_POWERMAC
 	tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)"
-	depends on SND && I2C && INPUT && PPC_PMAC
+	depends on I2C && INPUT && PPC_PMAC
 	select SND_PCM
 	help
 	  Say Y here to include support for the integrated sound device.
@@ -32,14 +32,9 @@
 	  Note that you can turn on/off DRC manually even without this
 	  option.
 
-endmenu
-
-menu "ALSA PowerPC devices"
-	depends on SND!=n && ( PPC64 || PPC32 )
-
 config SND_PS3
 	tristate "PS3 Audio support"
-	depends on SND && PS3_PS3AV
+	depends on PS3_PS3AV
 	select SND_PCM
 	default m
 	help
@@ -52,4 +47,5 @@
 	int "Startup delay time in ms"
 	depends on SND_PS3
 	default "2000"
-endmenu
+
+endif	# SND_PPC
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index ca94529..8a5b290 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -249,9 +249,7 @@
 	int i, err;
 	struct pmac_daca *mix;
 
-#ifdef CONFIG_KMOD
 	request_module("i2c-powermac");
-#endif /* CONFIG_KMOD */
 
 	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
 	if (! mix)
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 3f8d716..009df8d 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1350,9 +1350,7 @@
 	struct device_node *tas_node, *np;
 	char *chipname;
 
-#ifdef CONFIG_KMOD
 	request_module("i2c-powermac");
-#endif /* CONFIG_KMOD */
 
 	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
 	if (! mix)
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index b7e08ef..cfc1439 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -1,14 +1,22 @@
 # ALSA SH drivers
 
-menu "SUPERH devices"
-	depends on SND!=n && SUPERH
+menuconfig SND_SUPERH
+	bool "SUPERH sound devices"
+	depends on SUPERH
+	default y
+	help
+	  Support for sound devices specific to SUPERH architectures.
+	  Drivers that are implemented on ASoC can be found in
+	  "ALSA for SoC audio support" section.
+
+if SND_SUPERH
 
 config SND_AICA
 	tristate "Dreamcast Yamaha AICA sound"
-	depends on SH_DREAMCAST && SND
+	depends on SH_DREAMCAST
 	select SND_PCM
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
-endmenu
+endif	# SND_SUPERH
 
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 18f28ac..f743530 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -2,15 +2,8 @@
 # SoC audio configuration
 #
 
-menu "System on Chip audio support"
-	depends on SND!=n
-
-config SND_SOC_AC97_BUS
-	bool
-
-config SND_SOC
+menuconfig SND_SOC
 	tristate "ALSA for SoC audio support"
-	depends on SND
 	select SND_PCM
 	---help---
 
@@ -23,8 +16,15 @@
 	  This ASoC audio support can also be built as a module.  If so, the module
 	  will be called snd-soc-core.
 
+if SND_SOC
+
+config SND_SOC_AC97_BUS
+	bool
+
 # All the supported Soc's
+source "sound/soc/at32/Kconfig"
 source "sound/soc/at91/Kconfig"
+source "sound/soc/au1x/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/sh/Kconfig"
@@ -35,4 +35,5 @@
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
 
-endmenu
+endif	# SND_SOC
+
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 782db21..933a66d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,5 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
-obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ omap/
+obj-$(CONFIG_SND_SOC)	+= codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
+obj-$(CONFIG_SND_SOC)	+= omap/ au1x/
diff --git a/sound/soc/at32/Kconfig b/sound/soc/at32/Kconfig
new file mode 100644
index 0000000..b0765e8
--- /dev/null
+++ b/sound/soc/at32/Kconfig
@@ -0,0 +1,34 @@
+config SND_AT32_SOC
+        tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
+        depends on AVR32 && SND_SOC
+        help
+          Say Y or M if you want to add support for codecs attached to 
+          the AT32 SSC interface.  You will also need to
+          to select the audio interfaces to support below.
+
+
+config SND_AT32_SOC_SSC
+        tristate
+
+
+
+config SND_AT32_SOC_PLAYPAQ
+        tristate "SoC Audio support for PlayPaq with WM8510"
+        depends on SND_AT32_SOC && BOARD_PLAYPAQ
+        select SND_AT32_SOC_SSC
+        select SND_SOC_WM8510
+        help
+          Say Y or M here if you want to add support for SoC audio
+          on the LRS PlayPaq.
+
+
+
+config SND_AT32_SOC_PLAYPAQ_SLAVE
+        bool "Run CODEC on PlayPaq in slave mode"
+        depends on SND_AT32_SOC_PLAYPAQ
+        default n
+        help
+          Say Y if you want to run with the AT32 SSC generating the BCLK
+          and FRAME signals on the PlayPaq.  Unless you want to play
+          with the AT32 as the SSC master, you probably want to say N here,
+          as this will give you better sound quality.
diff --git a/sound/soc/at32/Makefile b/sound/soc/at32/Makefile
new file mode 100644
index 0000000..c03e55e
--- /dev/null
+++ b/sound/soc/at32/Makefile
@@ -0,0 +1,11 @@
+# AT32 Platform Support
+snd-soc-at32-objs := at32-pcm.o
+snd-soc-at32-ssc-objs := at32-ssc.o
+
+obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
+obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
+
+# AT32 Machine Support
+snd-soc-playpaq-objs := playpaq_wm8510.o
+
+obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c
new file mode 100644
index 0000000..435f1da
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.c
@@ -0,0 +1,491 @@
+/* sound/soc/at32/at32-pcm.c
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Note that this is basically a port of the sound/soc/at91-pcm.c to
+ * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "at32-pcm.h"
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ *	 them against real values for AT32
+ */
+static const struct snd_pcm_hardware at32_pcm_hardware = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		 SNDRV_PCM_INFO_PAUSE),
+
+	.formats = SNDRV_PCM_FMTBIT_S16,
+	.period_bytes_min = 32,
+	.period_bytes_max = 8192,	/* 512 frames * 16 bytes / frame */
+	.periods_min = 2,
+	.periods_max = 1024,
+	.buffer_bytes_max = 32 * 1024,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct at32_runtime_data {
+	struct at32_pcm_dma_params *params;
+	dma_addr_t dma_buffer;	/* physical address of DMA buffer */
+	dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
+	size_t period_size;
+
+	dma_addr_t period_ptr;	/* physical address of next period */
+	int periods;		/* period index of period_ptr */
+
+	/* Save PDC registers (for power management) */
+	u32 pdc_xpr_save;
+	u32 pdc_xcr_save;
+	u32 pdc_xnpr_save;
+	u32 pdc_xncr_save;
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * Helper functions
+\*--------------------------------------------------------------------------*/
+static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
+	size_t size = at32_pcm_hardware.buffer_bytes_max;
+
+	dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dmabuf->dev.dev = pcm->card->dev;
+	dmabuf->private_data = NULL;
+	dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
+					  &dmabuf->addr, GFP_KERNEL);
+	pr_debug("at32_pcm: preallocate_dma_buffer: "
+		 "area=%p, addr=%p, size=%ld\n",
+		 (void *)dmabuf->area, (void *)dmabuf->addr, size);
+
+	if (!dmabuf->area)
+		return -ENOMEM;
+
+	dmabuf->bytes = size;
+	return 0;
+}
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *rtd = substream->runtime;
+	struct at32_runtime_data *prtd = rtd->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+	static int count;
+
+	count++;
+	if (ssc_sr & params->mask->ssc_endbuf) {
+		pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
+			   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			   "underrun" : "overrun", params->name, ssc_sr, count);
+
+		/* re-start the PDC */
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_disable);
+		prtd->period_ptr += prtd->period_size;
+		if (prtd->period_ptr >= prtd->dma_buffer_end)
+			prtd->period_ptr = prtd->dma_buffer;
+
+
+		ssc_writex(params->ssc->regs, params->pdc->xpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xcr,
+			   prtd->period_size / params->pdc_xfer_size);
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_enable);
+	}
+
+
+	if (ssc_sr & params->mask->ssc_endx) {
+		/* Load the PDC next pointer and counter registers */
+		prtd->period_ptr += prtd->period_size;
+		if (prtd->period_ptr >= prtd->dma_buffer_end)
+			prtd->period_ptr = prtd->dma_buffer;
+		ssc_writex(params->ssc->regs, params->pdc->xnpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xncr,
+			   prtd->period_size / params->pdc_xfer_size);
+	}
+
+
+	snd_pcm_period_elapsed(substream);
+}
+
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct at32_runtime_data *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	/* this may get called several times by oss emulation
+	 * with different params
+	 */
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	prtd->params = rtd->dai->cpu_dai->dma_data;
+	prtd->params->dma_intr_handler = at32_pcm_dma_irq;
+
+	prtd->dma_buffer = runtime->dma_addr;
+	prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+	prtd->period_size = params_period_bytes(params);
+
+	pr_debug("hw_params: DMA for %s initialized "
+		 "(dma_bytes=%ld, period_size=%ld)\n",
+		 prtd->params->name, runtime->dma_bytes, prtd->period_size);
+
+	return 0;
+}
+
+
+
+static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct at32_runtime_data *prtd = substream->runtime->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+
+	if (params != NULL) {
+		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+			   params->mask->pdc_disable);
+		prtd->params->dma_intr_handler = NULL;
+	}
+
+	return 0;
+}
+
+
+
+static int at32_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct at32_runtime_data *prtd = substream->runtime->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+
+	ssc_writex(params->ssc->regs, SSC_IDR,
+		   params->mask->ssc_endx | params->mask->ssc_endbuf);
+	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+		   params->mask->pdc_disable);
+
+	return 0;
+}
+
+
+static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *rtd = substream->runtime;
+	struct at32_runtime_data *prtd = rtd->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+	int ret = 0;
+
+	pr_debug("at32_pcm_trigger: buffer_size = %ld, "
+		 "dma_area = %p, dma_bytes = %ld\n",
+		 rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		prtd->period_ptr = prtd->dma_buffer;
+
+		ssc_writex(params->ssc->regs, params->pdc->xpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xcr,
+			   prtd->period_size / params->pdc_xfer_size);
+
+		prtd->period_ptr += prtd->period_size;
+		ssc_writex(params->ssc->regs, params->pdc->xnpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xncr,
+			   prtd->period_size / params->pdc_xfer_size);
+
+		pr_debug("trigger: period_ptr=%lx, xpr=%x, "
+			 "xcr=%d, xnpr=%x, xncr=%d\n",
+			 (unsigned long)prtd->period_ptr,
+			 ssc_readx(params->ssc->regs, params->pdc->xpr),
+			 ssc_readx(params->ssc->regs, params->pdc->xcr),
+			 ssc_readx(params->ssc->regs, params->pdc->xnpr),
+			 ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+		ssc_writex(params->ssc->regs, SSC_IER,
+			   params->mask->ssc_endx | params->mask->ssc_endbuf);
+		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+			   params->mask->pdc_enable);
+
+		pr_debug("sr=%x, imr=%x\n",
+			 ssc_readx(params->ssc->regs, SSC_SR),
+			 ssc_readx(params->ssc->regs, SSC_IER));
+		break;		/* SNDRV_PCM_TRIGGER_START */
+
+
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_disable);
+		break;
+
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_enable);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+
+
+static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct at32_runtime_data *prtd = runtime->private_data;
+	struct at32_pcm_dma_params *params = prtd->params;
+	dma_addr_t ptr;
+	snd_pcm_uframes_t x;
+
+	ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+	x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+	if (x == runtime->buffer_size)
+		x = 0;
+
+	return x;
+}
+
+
+
+static int at32_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct at32_runtime_data *prtd;
+	int ret = 0;
+
+	snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
+
+	/* ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto out;
+
+	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+	if (prtd == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	runtime->private_data = prtd;
+
+
+out:
+	return ret;
+}
+
+
+
+static int at32_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct at32_runtime_data *prtd = substream->runtime->private_data;
+
+	kfree(prtd);
+	return 0;
+}
+
+
+static int at32_pcm_mmap(struct snd_pcm_substream *substream,
+			 struct vm_area_struct *vma)
+{
+	return remap_pfn_range(vma, vma->vm_start,
+			       substream->dma_buffer.addr >> PAGE_SHIFT,
+			       vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+
+
+static struct snd_pcm_ops at32_pcm_ops = {
+	.open = at32_pcm_open,
+	.close = at32_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = at32_pcm_hw_params,
+	.hw_free = at32_pcm_hw_free,
+	.prepare = at32_pcm_prepare,
+	.trigger = at32_pcm_trigger,
+	.pointer = at32_pcm_pointer,
+	.mmap = at32_pcm_mmap,
+};
+
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+static u64 at32_pcm_dmamask = 0xffffffff;
+
+static int at32_pcm_new(struct snd_card *card,
+			struct snd_soc_dai *dai,
+			struct snd_pcm *pcm)
+{
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &at32_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (dai->playback.channels_min) {
+		ret = at32_pcm_preallocate_dma_buffer(
+			  pcm, SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
+		ret = at32_pcm_preallocate_dma_buffer(
+			  pcm, SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+
+
+out:
+	return ret;
+}
+
+
+
+static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (substream == NULL)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+				  buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_pcm_suspend(struct platform_device *pdev,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = dai->runtime;
+	struct at32_runtime_data *prtd;
+	struct at32_pcm_dma_params *params;
+
+	if (runtime == NULL)
+		return 0;
+	prtd = runtime->private_data;
+	params = prtd->params;
+
+	/* Disable the PDC and save the PDC registers */
+	ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
+
+	prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+	prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+	prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+	prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+	return 0;
+}
+
+
+
+static int at32_pcm_resume(struct platform_device *pdev,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = dai->runtime;
+	struct at32_runtime_data *prtd;
+	struct at32_pcm_dma_params *params;
+
+	if (runtime == NULL)
+		return 0;
+	prtd = runtime->private_data;
+	params = prtd->params;
+
+	/* Restore the PDC registers and enable the PDC */
+	ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+	ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+	return 0;
+}
+#else /* CONFIG_PM */
+#  define at32_pcm_suspend	NULL
+#  define at32_pcm_resume	NULL
+#endif /* CONFIG_PM */
+
+
+
+struct snd_soc_platform at32_soc_platform = {
+	.name = "at32-audio",
+	.pcm_ops = &at32_pcm_ops,
+	.pcm_new = at32_pcm_new,
+	.pcm_free = at32_pcm_free_dma_buffers,
+	.suspend = at32_pcm_suspend,
+	.resume = at32_pcm_resume,
+};
+EXPORT_SYMBOL_GPL(at32_soc_platform);
+
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("Atmel AT32 PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-pcm.h b/sound/soc/at32/at32-pcm.h
new file mode 100644
index 0000000..2a52430
--- /dev/null
+++ b/sound/soc/at32/at32-pcm.h
@@ -0,0 +1,79 @@
+/* sound/soc/at32/at32-pcm.h
+ * ASoC PCM interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __SOUND_SOC_AT32_AT32_PCM_H
+#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
+
+#include <linux/atmel-ssc.h>
+
+
+/*
+ * Registers and status bits that are required by the PCM driver
+ * TODO: Is ptcr really used?
+ */
+struct at32_pdc_regs {
+	u32 xpr;		/* PDC RX/TX pointer */
+	u32 xcr;		/* PDC RX/TX counter */
+	u32 xnpr;		/* PDC next RX/TX pointer */
+	u32 xncr;		/* PDC next RX/TX counter */
+	u32 ptcr;		/* PDC transfer control */
+};
+
+
+
+/*
+ * SSC mask info
+ */
+struct at32_ssc_mask {
+	u32 ssc_enable;		/* SSC RX/TX enable */
+	u32 ssc_disable;	/* SSC RX/TX disable */
+	u32 ssc_endx;		/* SSC ENDTX or ENDRX */
+	u32 ssc_endbuf;		/* SSC TXBUFF or RXBUFF */
+	u32 pdc_enable;		/* PDC RX/TX enable */
+	u32 pdc_disable;	/* PDC RX/TX disable */
+};
+
+
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation.  All fields except dma_intr_handler() are initialized
+ * by the interface.  The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct at32_pcm_dma_params {
+	char *name;		/* stream identifier */
+	int pdc_xfer_size;	/* PDC counter increment in bytes */
+	struct ssc_device *ssc;	/* SSC device for stream */
+	struct at32_pdc_regs *pdc;	/* PDC register info */
+	struct at32_ssc_mask *mask;	/* SSC mask info */
+	struct snd_pcm_substream *substream;
+	void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
+};
+
+
+
+/*
+ * The AT32 ASoC platform driver
+ */
+extern struct snd_soc_platform at32_soc_platform;
+
+
+
+/*
+ * SSC register access (since ssc_writel() / ssc_readl() require literal name)
+ */
+#define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
+#define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
+
+#endif /* __SOUND_SOC_AT32_AT32_PCM_H */
diff --git a/sound/soc/at32/at32-ssc.c b/sound/soc/at32/at32-ssc.c
new file mode 100644
index 0000000..4ef6492
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.c
@@ -0,0 +1,849 @@
+/* sound/soc/at32/at32-ssc.c
+ * ASoC platform driver for AT32 using SSC as DAI
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Note that this is basically a port of the sound/soc/at91-ssc.c to
+ * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
+ */
+
+/* #define DEBUG */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Constants
+\*-------------------------------------------------------------------------*/
+#define NUM_SSC_DEVICES		3
+
+/*
+ * SSC direction masks
+ */
+#define SSC_DIR_MASK_UNUSED	0
+#define SSC_DIR_MASK_PLAYBACK	1
+#define SSC_DIR_MASK_CAPTURE	2
+
+/*
+ * SSC register values that Atmel left out of <linux/atmel-ssc.h>.  These
+ * are expected to be used with SSC_BF
+ */
+/* START bit field values */
+#define SSC_START_CONTINUOUS	0
+#define SSC_START_TX_RX		1
+#define SSC_START_LOW_RF	2
+#define SSC_START_HIGH_RF	3
+#define SSC_START_FALLING_RF	4
+#define SSC_START_RISING_RF	5
+#define SSC_START_LEVEL_RF	6
+#define SSC_START_EDGE_RF	7
+#define SSS_START_COMPARE_0	8
+
+/* CKI bit field values */
+#define SSC_CKI_FALLING		0
+#define SSC_CKI_RISING		1
+
+/* CKO bit field values */
+#define SSC_CKO_NONE		0
+#define SSC_CKO_CONTINUOUS	1
+#define SSC_CKO_TRANSFER	2
+
+/* CKS bit field values */
+#define SSC_CKS_DIV		0
+#define SSC_CKS_CLOCK		1
+#define SSC_CKS_PIN		2
+
+/* FSEDGE bit field values */
+#define SSC_FSEDGE_POSITIVE	0
+#define SSC_FSEDGE_NEGATIVE	1
+
+/* FSOS bit field values */
+#define SSC_FSOS_NONE		0
+#define SSC_FSOS_NEGATIVE	1
+#define SSC_FSOS_POSITIVE	2
+#define SSC_FSOS_LOW		3
+#define SSC_FSOS_HIGH		4
+#define SSC_FSOS_TOGGLE		5
+
+#define START_DELAY		1
+
+
+
+/*-------------------------------------------------------------------------*\
+ * Module data
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC PDC registered required by the PCM DMA engine
+ */
+static struct at32_pdc_regs pdc_tx_reg = {
+	.xpr = SSC_PDC_TPR,
+	.xcr = SSC_PDC_TCR,
+	.xnpr = SSC_PDC_TNPR,
+	.xncr = SSC_PDC_TNCR,
+};
+
+
+
+static struct at32_pdc_regs pdc_rx_reg = {
+	.xpr = SSC_PDC_RPR,
+	.xcr = SSC_PDC_RCR,
+	.xnpr = SSC_PDC_RNPR,
+	.xncr = SSC_PDC_RNCR,
+};
+
+
+
+/*
+ * SSC and PDC status bits for transmit and receive
+ */
+static struct at32_ssc_mask ssc_tx_mask = {
+	.ssc_enable = SSC_BIT(CR_TXEN),
+	.ssc_disable = SSC_BIT(CR_TXDIS),
+	.ssc_endx = SSC_BIT(SR_ENDTX),
+	.ssc_endbuf = SSC_BIT(SR_TXBUFE),
+	.pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
+	.pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
+};
+
+
+
+static struct at32_ssc_mask ssc_rx_mask = {
+	.ssc_enable = SSC_BIT(CR_RXEN),
+	.ssc_disable = SSC_BIT(CR_RXDIS),
+	.ssc_endx = SSC_BIT(SR_ENDRX),
+	.ssc_endbuf = SSC_BIT(SR_RXBUFF),
+	.pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
+	.pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
+};
+
+
+
+/*
+ * DMA parameters for each SSC
+ */
+static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+	{
+	 {
+	  .name = "SSC0 PCM out",
+	  .pdc = &pdc_tx_reg,
+	  .mask = &ssc_tx_mask,
+	  },
+	 {
+	  .name = "SSC0 PCM in",
+	  .pdc = &pdc_rx_reg,
+	  .mask = &ssc_rx_mask,
+	  },
+	 },
+	{
+	 {
+	  .name = "SSC1 PCM out",
+	  .pdc = &pdc_tx_reg,
+	  .mask = &ssc_tx_mask,
+	  },
+	 {
+	  .name = "SSC1 PCM in",
+	  .pdc = &pdc_rx_reg,
+	  .mask = &ssc_rx_mask,
+	  },
+	 },
+	{
+	 {
+	  .name = "SSC2 PCM out",
+	  .pdc = &pdc_tx_reg,
+	  .mask = &ssc_tx_mask,
+	  },
+	 {
+	  .name = "SSC2 PCM in",
+	  .pdc = &pdc_rx_reg,
+	  .mask = &ssc_rx_mask,
+	  },
+	 },
+};
+
+
+
+static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
+	{
+	 .name = "ssc0",
+	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
+	 .dir_mask = SSC_DIR_MASK_UNUSED,
+	 .initialized = 0,
+	 },
+	{
+	 .name = "ssc1",
+	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
+	 .dir_mask = SSC_DIR_MASK_UNUSED,
+	 .initialized = 0,
+	 },
+	{
+	 .name = "ssc2",
+	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
+	 .dir_mask = SSC_DIR_MASK_UNUSED,
+	 .initialized = 0,
+	 },
+};
+
+
+
+
+/*-------------------------------------------------------------------------*\
+ * ISR
+\*-------------------------------------------------------------------------*/
+/*
+ * SSC interrupt handler.  Passes PDC interrupts to the DMA interrupt
+ * handler in the PCM driver.
+ */
+static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
+{
+	struct at32_ssc_info *ssc_p = dev_id;
+	struct at32_pcm_dma_params *dma_params;
+	u32 ssc_sr;
+	u32 ssc_substream_mask;
+	int i;
+
+	ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
+		  ssc_readl(ssc_p->ssc->regs, IMR));
+
+	/*
+	 * Loop through substreams attached to this SSC.  If a DMA-related
+	 * interrupt occured on that substream, call the DMA interrupt
+	 * handler function, if one has been registered in the dma_param
+	 * structure by the PCM driver.
+	 */
+	for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+		dma_params = ssc_p->dma_params[i];
+
+		if ((dma_params != NULL) &&
+		    (dma_params->dma_intr_handler != NULL)) {
+			ssc_substream_mask = (dma_params->mask->ssc_endx |
+					      dma_params->mask->ssc_endbuf);
+			if (ssc_sr & ssc_substream_mask) {
+				dma_params->dma_intr_handler(ssc_sr,
+							     dma_params->
+							     substream);
+			}
+		}
+	}
+
+
+	return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*\
+ * DAI functions
+\*-------------------------------------------------------------------------*/
+/*
+ * Startup.  Only that one substream allowed in each direction.
+ */
+static int at32_ssc_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	int dir_mask;
+
+	dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		    SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
+
+	spin_lock_irq(&ssc_p->lock);
+	if (ssc_p->dir_mask & dir_mask) {
+		spin_unlock_irq(&ssc_p->lock);
+		return -EBUSY;
+	}
+	ssc_p->dir_mask |= dir_mask;
+	spin_unlock_irq(&ssc_p->lock);
+
+	return 0;
+}
+
+
+
+/*
+ * Shutdown.  Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	struct at32_pcm_dma_params *dma_params;
+	int dir_mask;
+
+	dma_params = ssc_p->dma_params[substream->stream];
+
+	if (dma_params != NULL) {
+		ssc_writel(dma_params->ssc->regs, CR,
+			   dma_params->mask->ssc_disable);
+		pr_debug("%s disabled SSC_SR=0x%08x\n",
+			 (substream->stream ? "receiver" : "transmit"),
+			 ssc_readl(ssc_p->ssc->regs, SR));
+
+		dma_params->ssc = NULL;
+		dma_params->substream = NULL;
+		ssc_p->dma_params[substream->stream] = NULL;
+	}
+
+
+	dir_mask = 1 << substream->stream;
+	spin_lock_irq(&ssc_p->lock);
+	ssc_p->dir_mask &= ~dir_mask;
+	if (!ssc_p->dir_mask) {
+		/* Shutdown the SSC clock */
+		pr_debug("at32-ssc: Stopping user %d clock\n",
+			 ssc_p->ssc->user);
+		clk_disable(ssc_p->ssc->clk);
+
+		if (ssc_p->initialized) {
+			free_irq(ssc_p->ssc->irq, ssc_p);
+			ssc_p->initialized = 0;
+		}
+
+		/* Reset the SSC */
+		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+		/* clear the SSC dividers */
+		ssc_p->cmr_div = 0;
+		ssc_p->tcmr_period = 0;
+		ssc_p->rcmr_period = 0;
+	}
+	spin_unlock_irq(&ssc_p->lock);
+}
+
+
+
+/*
+ * Set the SSC system clock rate
+ */
+static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+				   int clk_id, unsigned int freq, int dir)
+{
+	/* TODO: What the heck do I do here? */
+	return 0;
+}
+
+
+
+/*
+ * Record DAI format for use by hw_params()
+ */
+static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+				unsigned int fmt)
+{
+	struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+	ssc_p->daifmt = fmt;
+	return 0;
+}
+
+
+
+/*
+ * Record SSC clock dividers for use in hw_params()
+ */
+static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+				   int div_id, int div)
+{
+	struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+	switch (div_id) {
+	case AT32_SSC_CMR_DIV:
+		/*
+		 * The same master clock divider is used for both
+		 * transmit and receive, so if a value has already
+		 * been set, it must match this value
+		 */
+		if (ssc_p->cmr_div == 0)
+			ssc_p->cmr_div = div;
+		else if (div != ssc_p->cmr_div)
+			return -EBUSY;
+		break;
+
+	case AT32_SSC_TCMR_PERIOD:
+		ssc_p->tcmr_period = div;
+		break;
+
+	case AT32_SSC_RCMR_PERIOD:
+		ssc_p->rcmr_period = div;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * Configure the SSC
+ */
+static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int id = rtd->dai->cpu_dai->id;
+	struct at32_ssc_info *ssc_p = &ssc_info[id];
+	struct at32_pcm_dma_params *dma_params;
+	int channels, bits;
+	u32 tfmr, rfmr, tcmr, rcmr;
+	int start_event;
+	int ret;
+
+
+	/*
+	 * Currently, there is only one set of dma_params for each direction.
+	 * If more are added, this code will have to be changed to select
+	 * the proper set
+	 */
+	dma_params = &ssc_dma_params[id][substream->stream];
+	dma_params->ssc = ssc_p->ssc;
+	dma_params->substream = substream;
+
+	ssc_p->dma_params[substream->stream] = dma_params;
+
+
+	/*
+	 * The cpu_dai->dma_data field is only used to communicate the
+	 * appropriate DMA parameters to the PCM driver's hw_params()
+	 * function.  It should not be used for other purposes as it
+	 * is common to all substreams.
+	 */
+	rtd->dai->cpu_dai->dma_data = dma_params;
+
+	channels = params_channels(params);
+
+
+	/*
+	 * Determine sample size in bits and the PDC increment
+	 */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		bits = 8;
+		dma_params->pdc_xfer_size = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16:
+		bits = 16;
+		dma_params->pdc_xfer_size = 2;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24:
+		bits = 24;
+		dma_params->pdc_xfer_size = 4;
+		break;
+
+	case SNDRV_PCM_FORMAT_S32:
+		bits = 32;
+		dma_params->pdc_xfer_size = 4;
+		break;
+
+	default:
+		pr_warning("at32-ssc: Unsupported PCM format %d",
+			   params_format(params));
+		return -EINVAL;
+	}
+	pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
+		 bits, dma_params->pdc_xfer_size, channels);
+
+
+	/*
+	 * The SSC only supports up to 16-bit samples in I2S format, due
+	 * to the size of the Frame Mode Register FSLEN field.
+	 */
+	if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+		if (bits > 16) {
+			pr_warning("at32-ssc: "
+				   "sample size %d is too large for I2S\n",
+				   bits);
+			return -EINVAL;
+		}
+
+
+	/*
+	 * Compute the SSC register settings
+	 */
+	switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+				 SND_SOC_DAIFMT_MASTER_MASK)) {
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+		/*
+		 * I2S format, SSC provides BCLK and LRS clocks.
+		 *
+		 * The SSC transmit and receive clocks are generated from the
+		 * MCK divider, and the BCLK signal is output on the SSC TK line
+		 */
+		pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
+		rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+			SSC_BF(RCMR_STTDLY, START_DELAY) |
+			SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
+			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
+			SSC_BF(RFMR_FSLEN, bits - 1) |
+			SSC_BF(RFMR_DATNB, channels - 1) |
+			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+		tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+			SSC_BF(TCMR_STTDLY, START_DELAY) |
+			SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
+			SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+			SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+			SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
+			SSC_BF(TFMR_FSLEN, bits - 1) |
+			SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
+			SSC_BF(TFMR_DATLEN, bits - 1));
+		break;
+
+
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+		/*
+		 * I2S format, CODEC supplies BCLK and LRC clock.
+		 *
+		 * The SSC transmit clock is obtained from the BCLK signal
+		 * on the TK line, and the SSC receive clock is generated from
+		 * the transmit clock.
+		 *
+		 * For single channel data, one sample is transferred on the
+		 * falling edge of the LRC clock.  For two channel data, one
+		 * sample is transferred on both edges of the LRC clock.
+		 */
+		pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
+		start_event = ((channels == 1) ?
+			       SSC_START_FALLING_RF : SSC_START_EDGE_RF);
+
+		rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
+			SSC_BF(RCMR_START, start_event) |
+			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
+
+		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
+			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+		tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
+			SSC_BF(TCMR_START, start_event) |
+			SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
+			SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(TCMR_CKS, SSC_CKS_PIN));
+
+		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
+			SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+		break;
+
+
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+		/*
+		 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+		 *
+		 * The SSC transmit and receive clocks are generated from the
+		 * MCK divider, and the BCLK signal is output on the SSC TK line
+		 */
+		pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
+		rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
+			SSC_BF(RCMR_STTDLY, 1) |
+			SSC_BF(RCMR_START, SSC_START_RISING_RF) |
+			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
+			SSC_BF(RCMR_CKS, SSC_CKS_DIV));
+
+		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
+			SSC_BF(RFMR_DATNB, channels - 1) |
+			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
+
+		tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
+			SSC_BF(TCMR_STTDLY, 1) |
+			SSC_BF(TCMR_START, SSC_START_RISING_RF) |
+			SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
+			SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
+			SSC_BF(TCMR_CKS, SSC_CKS_DIV));
+
+		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
+			SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
+			SSC_BF(TFMR_DATNB, channels - 1) |
+			SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
+		break;
+
+
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+	default:
+		pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
+			   ssc_p->daifmt);
+		return -EINVAL;
+		break;
+	}
+	pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
+		 rcmr, rfmr, tcmr, tfmr);
+
+
+	if (!ssc_p->initialized) {
+		/* enable peripheral clock */
+		pr_debug("at32-ssc: Starting clock\n");
+		clk_enable(ssc_p->ssc->clk);
+
+		/* Reset the SSC and its PDC registers */
+		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+		ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
+
+		ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
+
+		ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
+				  ssc_p->name, ssc_p);
+		if (ret < 0) {
+			pr_warning("at32-ssc: request irq failed (%d)\n", ret);
+			pr_debug("at32-ssc: Stopping clock\n");
+			clk_disable(ssc_p->ssc->clk);
+			return ret;
+		}
+
+		ssc_p->initialized = 1;
+	}
+
+	/* Set SSC clock mode register */
+	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+
+	/* set receive clock mode and format */
+	ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
+	ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
+
+	/* set transmit clock mode and format */
+	ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
+	ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
+
+	pr_debug("at32-ssc: SSC initialized\n");
+	return 0;
+}
+
+
+
+static int at32_ssc_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	struct at32_pcm_dma_params *dma_params;
+
+	dma_params = ssc_p->dma_params[substream->stream];
+
+	ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
+
+	return 0;
+}
+
+
+
+#ifdef CONFIG_PM
+static int at32_ssc_suspend(struct platform_device *pdev,
+			    struct snd_soc_dai *cpu_dai)
+{
+	struct at32_ssc_info *ssc_p;
+
+	if (!cpu_dai->active)
+		return 0;
+
+	ssc_p = &ssc_info[cpu_dai->id];
+
+	/* Save the status register before disabling transmit and receive */
+	ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
+	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
+
+	/* Save the current interrupt mask, then disable unmasked interrupts */
+	ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
+	ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
+
+	ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
+	ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
+	ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
+	ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
+	ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+
+	return 0;
+}
+
+
+
+static int at32_ssc_resume(struct platform_device *pdev,
+			   struct snd_soc_dai *cpu_dai)
+{
+	struct at32_ssc_info *ssc_p;
+	u32 cr;
+
+	if (!cpu_dai->active)
+		return 0;
+
+	ssc_p = &ssc_info[cpu_dai->id];
+
+	/* restore SSC register settings */
+	ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
+	ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
+	ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
+	ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
+	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
+
+	/* re-enable interrupts */
+	ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
+
+	/* Re-enable recieve and transmit as appropriate */
+	cr = 0;
+	cr |=
+	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
+	cr |=
+	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
+	ssc_writel(ssc_p->ssc->regs, CR, cr);
+
+	return 0;
+}
+#else /* CONFIG_PM */
+#  define at32_ssc_suspend	NULL
+#  define at32_ssc_resume	NULL
+#endif /* CONFIG_PM */
+
+
+#define AT32_SSC_RATES \
+    (SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
+     SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+     SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+
+#define AT32_SSC_FORMATS \
+    (SNDRV_PCM_FMTBIT_S8  | SNDRV_PCM_FMTBIT_S16 | \
+     SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
+
+
+struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
+	{
+	 .name = "at32-ssc0",
+	 .id = 0,
+	 .type = SND_SOC_DAI_PCM,
+	 .suspend = at32_ssc_suspend,
+	 .resume = at32_ssc_resume,
+	 .playback = {
+		      .channels_min = 1,
+		      .channels_max = 2,
+		      .rates = AT32_SSC_RATES,
+		      .formats = AT32_SSC_FORMATS,
+		      },
+	 .capture = {
+		     .channels_min = 1,
+		     .channels_max = 2,
+		     .rates = AT32_SSC_RATES,
+		     .formats = AT32_SSC_FORMATS,
+		     },
+	 .ops = {
+		 .startup = at32_ssc_startup,
+		 .shutdown = at32_ssc_shutdown,
+		 .prepare = at32_ssc_prepare,
+		 .hw_params = at32_ssc_hw_params,
+		 },
+	 .dai_ops = {
+		     .set_sysclk = at32_ssc_set_dai_sysclk,
+		     .set_fmt = at32_ssc_set_dai_fmt,
+		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
+		     },
+	 .private_data = &ssc_info[0],
+	 },
+	{
+	 .name = "at32-ssc1",
+	 .id = 1,
+	 .type = SND_SOC_DAI_PCM,
+	 .suspend = at32_ssc_suspend,
+	 .resume = at32_ssc_resume,
+	 .playback = {
+		      .channels_min = 1,
+		      .channels_max = 2,
+		      .rates = AT32_SSC_RATES,
+		      .formats = AT32_SSC_FORMATS,
+		      },
+	 .capture = {
+		     .channels_min = 1,
+		     .channels_max = 2,
+		     .rates = AT32_SSC_RATES,
+		     .formats = AT32_SSC_FORMATS,
+		     },
+	 .ops = {
+		 .startup = at32_ssc_startup,
+		 .shutdown = at32_ssc_shutdown,
+		 .prepare = at32_ssc_prepare,
+		 .hw_params = at32_ssc_hw_params,
+		 },
+	 .dai_ops = {
+		     .set_sysclk = at32_ssc_set_dai_sysclk,
+		     .set_fmt = at32_ssc_set_dai_fmt,
+		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
+		     },
+	 .private_data = &ssc_info[1],
+	 },
+	{
+	 .name = "at32-ssc2",
+	 .id = 2,
+	 .type = SND_SOC_DAI_PCM,
+	 .suspend = at32_ssc_suspend,
+	 .resume = at32_ssc_resume,
+	 .playback = {
+		      .channels_min = 1,
+		      .channels_max = 2,
+		      .rates = AT32_SSC_RATES,
+		      .formats = AT32_SSC_FORMATS,
+		      },
+	 .capture = {
+		     .channels_min = 1,
+		     .channels_max = 2,
+		     .rates = AT32_SSC_RATES,
+		     .formats = AT32_SSC_FORMATS,
+		     },
+	 .ops = {
+		 .startup = at32_ssc_startup,
+		 .shutdown = at32_ssc_shutdown,
+		 .prepare = at32_ssc_prepare,
+		 .hw_params = at32_ssc_hw_params,
+		 },
+	 .dai_ops = {
+		     .set_sysclk = at32_ssc_set_dai_sysclk,
+		     .set_fmt = at32_ssc_set_dai_fmt,
+		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
+		     },
+	 .private_data = &ssc_info[2],
+	 },
+};
+EXPORT_SYMBOL_GPL(at32_ssc_dai);
+
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-ssc.h b/sound/soc/at32/at32-ssc.h
new file mode 100644
index 0000000..3c052db
--- /dev/null
+++ b/sound/soc/at32/at32-ssc.h
@@ -0,0 +1,59 @@
+/* sound/soc/at32/at32-ssc.h
+ * ASoC SSC interface for Atmel AT32 SoC
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __SOUND_SOC_AT32_AT32_SSC_H
+#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
+
+#include <linux/types.h>
+#include <linux/atmel-ssc.h>
+
+#include "at32-pcm.h"
+
+
+
+struct at32_ssc_state {
+	u32 ssc_cmr;
+	u32 ssc_rcmr;
+	u32 ssc_rfmr;
+	u32 ssc_tcmr;
+	u32 ssc_tfmr;
+	u32 ssc_sr;
+	u32 ssc_imr;
+};
+
+
+
+struct at32_ssc_info {
+	char *name;
+	struct ssc_device *ssc;
+	spinlock_t lock;	/* lock for dir_mask */
+	unsigned short dir_mask;	/* 0=unused, 1=playback, 2=capture */
+	unsigned short initialized;	/* true if SSC has been initialized */
+	unsigned short daifmt;
+	unsigned short cmr_div;
+	unsigned short tcmr_period;
+	unsigned short rcmr_period;
+	struct at32_pcm_dma_params *dma_params[2];
+	struct at32_ssc_state ssc_state;
+};
+
+
+/* SSC divider ids */
+#define AT32_SSC_CMR_DIV        0	/* MCK divider for BCLK */
+#define AT32_SSC_TCMR_PERIOD    1	/* BCLK divider for transmit FS */
+#define AT32_SSC_RCMR_PERIOD    2	/* BCLK divider for receive FS */
+
+
+extern struct snd_soc_dai at32_ssc_dai[];
+
+
+
+#endif /* __SOUND_SOC_AT32_AT32_SSC_H */
diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c
new file mode 100644
index 0000000..fee5f8e
--- /dev/null
+++ b/sound/soc/at32/playpaq_wm8510.c
@@ -0,0 +1,522 @@
+/* sound/soc/at32/playpaq_wm8510.c
+ * ASoC machine driver for PlayPaq using WM8510 codec
+ *
+ * Copyright (C) 2008 Long Range Systems
+ *    Geoffrey Wossum <gwossum@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This code is largely inspired by sound/soc/at91/eti_b1_wm8731.c
+ *
+ * NOTE: If you don't have the AT32 enhanced portmux configured (which
+ * isn't currently in the mainline or Atmel patched kernel), you will
+ * need to set the MCLK pin (PA30) to peripheral A in your board initialization
+ * code.  Something like:
+ *	at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/arch/at32ap700x.h>
+#include <asm/arch/portmux.h>
+
+#include "../codecs/wm8510.h"
+#include "at32-pcm.h"
+#include "at32-ssc.h"
+
+
+/*-------------------------------------------------------------------------*\
+ * constants
+\*-------------------------------------------------------------------------*/
+#define MCLK_PIN		GPIO_PIN_PA(30)
+#define MCLK_PERIPH		GPIO_PERIPH_A
+
+
+/*-------------------------------------------------------------------------*\
+ * data types
+\*-------------------------------------------------------------------------*/
+/* SSC clocking data */
+struct ssc_clock_data {
+	/* CMR div */
+	unsigned int cmr_div;
+
+	/* Frame period (as needed by xCMR.PERIOD) */
+	unsigned int period;
+
+	/* The SSC clock rate these settings where calculated for */
+	unsigned long ssc_rate;
+};
+
+
+/*-------------------------------------------------------------------------*\
+ * module data
+\*-------------------------------------------------------------------------*/
+static struct clk *_gclk0;
+static struct clk *_pll0;
+
+#define CODEC_CLK (_gclk0)
+
+
+/*-------------------------------------------------------------------------*\
+ * Sound SOC operations
+\*-------------------------------------------------------------------------*/
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *cpu_dai)
+{
+	struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+	struct ssc_device *ssc = ssc_p->ssc;
+	struct ssc_clock_data cd;
+	unsigned int rate, width_bits, channels;
+	unsigned int bitrate, ssc_div;
+	unsigned actual_rate;
+
+
+	/*
+	 * Figure out required bitrate
+	 */
+	rate = params_rate(params);
+	channels = params_channels(params);
+	width_bits = snd_pcm_format_physical_width(params_format(params));
+	bitrate = rate * width_bits * channels;
+
+
+	/*
+	 * Figure out required SSC divider and period for required bitrate
+	 */
+	cd.ssc_rate = clk_get_rate(ssc->clk);
+	ssc_div = cd.ssc_rate / bitrate;
+	cd.cmr_div = ssc_div / 2;
+	if (ssc_div & 1) {
+		/* round cmr_div up */
+		cd.cmr_div++;
+	}
+	cd.period = width_bits - 1;
+
+
+	/*
+	 * Find actual rate, compare to requested rate
+	 */
+	actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
+	pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
+		 rate, actual_rate);
+
+
+	return cd;
+}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+
+static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+	struct ssc_device *ssc = ssc_p->ssc;
+	unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
+	int ret;
+
+
+	/* Due to difficulties with getting the correct clocks from the AT32's
+	 * PLL0, we're going to let the CODEC be in charge of all the clocks
+	 */
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+	const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+#else
+	struct ssc_clock_data cd;
+	const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+#endif
+
+	if (ssc == NULL) {
+		pr_warning("playpaq_wm8510_hw_params: ssc is NULL!\n");
+		return -EINVAL;
+	}
+
+
+	/*
+	 * Figure out PLL and BCLK dividers for WM8510
+	 */
+	switch (params_rate(params)) {
+	case 48000:
+		pll_out = 12288000;
+		mclk_div = WM8510_MCLKDIV_1;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 44100:
+		pll_out = 11289600;
+		mclk_div = WM8510_MCLKDIV_1;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 22050:
+		pll_out = 11289600;
+		mclk_div = WM8510_MCLKDIV_2;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 16000:
+		pll_out = 12288000;
+		mclk_div = WM8510_MCLKDIV_3;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 11025:
+		pll_out = 11289600;
+		mclk_div = WM8510_MCLKDIV_4;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	case 8000:
+		pll_out = 12288000;
+		mclk_div = WM8510_MCLKDIV_6;
+		bclk = WM8510_BCLKDIV_8;
+		break;
+
+	default:
+		pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
+			   params_rate(params));
+		return -EINVAL;
+	}
+
+
+	/*
+	 * set CPU and CODEC DAI configuration
+	 */
+	ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: "
+			   "Failed to set CODEC DAI format (%d)\n",
+			   ret);
+		return ret;
+	}
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: "
+			   "Failed to set CPU DAI format (%d)\n",
+			   ret);
+		return ret;
+	}
+
+
+	/*
+	 * Set CPU clock configuration
+	 */
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+	cd = playpaq_wm8510_calc_ssc_clock(params, cpu_dai);
+	pr_debug("playpaq_wm8510: cmr_div = %d, period = %d\n",
+		 cd.cmr_div, cd.period);
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_CMR_DIV, cd.cmr_div);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: Failed to set CPU CMR_DIV (%d)\n",
+			   ret);
+		return ret;
+	}
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_TCMR_PERIOD,
+					  cd.period);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: "
+			   "Failed to set CPU transmit period (%d)\n",
+			   ret);
+		return ret;
+	}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+	/*
+	 * Set CODEC clock configuration
+	 */
+	pr_debug("playpaq_wm8510: "
+		 "pll_in = %ld, pll_out = %u, bclk = %x, mclk = %x\n",
+		 clk_get_rate(CODEC_CLK), pll_out, bclk, mclk_div);
+
+
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_BCLKDIV, bclk);
+	if (ret < 0) {
+		pr_warning
+		    ("playpaq_wm8510: Failed to set CODEC DAI BCLKDIV (%d)\n",
+		     ret);
+		return ret;
+	}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+	ret = snd_soc_dai_set_pll(codec_dai, 0,
+					 clk_get_rate(CODEC_CLK), pll_out);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
+			   ret);
+		return ret;
+	}
+
+
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_MCLKDIV, mclk_div);
+	if (ret < 0) {
+		pr_warning("playpaq_wm8510: Failed to set CODEC MCLKDIV (%d)\n",
+			   ret);
+		return ret;
+	}
+
+
+	return 0;
+}
+
+
+
+static struct snd_soc_ops playpaq_wm8510_ops = {
+	.hw_params = playpaq_wm8510_hw_params,
+};
+
+
+
+static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("Int Mic", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+
+
+static const char *intercon[][3] = {
+	/* speaker connected to SPKOUT */
+	{"Ext Spk", NULL, "SPKOUTP"},
+	{"Ext Spk", NULL, "SPKOUTN"},
+
+	{"Mic Bias", NULL, "Int Mic"},
+	{"MICN", NULL, "Mic Bias"},
+	{"MICP", NULL, "Mic Bias"},
+
+	/* Terminator */
+	{NULL, NULL, NULL},
+};
+
+
+
+static int playpaq_wm8510_init(struct snd_soc_codec *codec)
+{
+	int i;
+
+	/*
+	 * Add DAPM widgets
+	 */
+	for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
+		snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]);
+
+
+
+	/*
+	 * Setup audio path interconnects
+	 */
+	for (i = 0; intercon[i][0] != NULL; i++) {
+		snd_soc_dapm_connect_input(codec,
+					   intercon[i][0],
+					   intercon[i][1], intercon[i][2]);
+	}
+
+
+	/* always connected pins */
+	snd_soc_dapm_enable_pin(codec, "Int Mic");
+	snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	snd_soc_dapm_sync(codec);
+
+
+
+	/* Make CSB show PLL rate */
+	snd_soc_dai_set_clkdiv(codec->dai, WM8510_OPCLKDIV,
+				       WM8510_OPCLKDIV_1 | 4);
+
+	return 0;
+}
+
+
+
+static struct snd_soc_dai_link playpaq_wm8510_dai = {
+	.name = "WM8510",
+	.stream_name = "WM8510 PCM",
+	.cpu_dai = &at32_ssc_dai[0],
+	.codec_dai = &wm8510_dai,
+	.init = playpaq_wm8510_init,
+	.ops = &playpaq_wm8510_ops,
+};
+
+
+
+static struct snd_soc_machine snd_soc_machine_playpaq = {
+	.name = "LRS_PlayPaq_WM8510",
+	.dai_link = &playpaq_wm8510_dai,
+	.num_links = 1,
+};
+
+
+
+static struct wm8510_setup_data playpaq_wm8510_setup = {
+	.i2c_address = 0x1a,
+};
+
+
+
+static struct snd_soc_device playpaq_wm8510_snd_devdata = {
+	.machine = &snd_soc_machine_playpaq,
+	.platform = &at32_soc_platform,
+	.codec_dev = &soc_codec_dev_wm8510,
+	.codec_data = &playpaq_wm8510_setup,
+};
+
+static struct platform_device *playpaq_snd_device;
+
+
+static int __init playpaq_asoc_init(void)
+{
+	int ret = 0;
+	struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+	struct ssc_device *ssc = NULL;
+
+
+	/*
+	 * Request SSC device
+	 */
+	ssc = ssc_request(0);
+	if (IS_ERR(ssc)) {
+		ret = PTR_ERR(ssc);
+		ssc = NULL;
+		goto err_ssc;
+	}
+	ssc_p->ssc = ssc;
+
+
+	/*
+	 * Configure MCLK for WM8510
+	 */
+	_gclk0 = clk_get(NULL, "gclk0");
+	if (IS_ERR(_gclk0)) {
+		_gclk0 = NULL;
+		goto err_gclk0;
+	}
+	_pll0 = clk_get(NULL, "pll0");
+	if (IS_ERR(_pll0)) {
+		_pll0 = NULL;
+		goto err_pll0;
+	}
+	if (clk_set_parent(_gclk0, _pll0)) {
+		pr_warning("snd-soc-playpaq: "
+			   "Failed to set PLL0 as parent for DAC clock\n");
+		goto err_set_clk;
+	}
+	clk_set_rate(CODEC_CLK, 12000000);
+	clk_enable(CODEC_CLK);
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+	at32_select_periph(MCLK_PIN, MCLK_PERIPH, 0);
+#endif
+
+
+	/*
+	 * Create and register platform device
+	 */
+	playpaq_snd_device = platform_device_alloc("soc-audio", 0);
+	if (playpaq_snd_device == NULL) {
+		ret = -ENOMEM;
+		goto err_device_alloc;
+	}
+
+	platform_set_drvdata(playpaq_snd_device, &playpaq_wm8510_snd_devdata);
+	playpaq_wm8510_snd_devdata.dev = &playpaq_snd_device->dev;
+
+	ret = platform_device_add(playpaq_snd_device);
+	if (ret) {
+		pr_warning("playpaq_wm8510: platform_device_add failed (%d)\n",
+			   ret);
+		goto err_device_add;
+	}
+
+	return 0;
+
+
+err_device_add:
+	if (playpaq_snd_device != NULL) {
+		platform_device_put(playpaq_snd_device);
+		playpaq_snd_device = NULL;
+	}
+err_device_alloc:
+err_set_clk:
+	if (_pll0 != NULL) {
+		clk_put(_pll0);
+		_pll0 = NULL;
+	}
+err_pll0:
+	if (_gclk0 != NULL) {
+		clk_put(_gclk0);
+		_gclk0 = NULL;
+	}
+err_gclk0:
+	if (ssc != NULL) {
+		ssc_free(ssc);
+		ssc = NULL;
+	}
+err_ssc:
+	return ret;
+}
+
+
+static void __exit playpaq_asoc_exit(void)
+{
+	struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+	struct ssc_device *ssc;
+
+	if (ssc_p != NULL) {
+		ssc = ssc_p->ssc;
+		if (ssc != NULL)
+			ssc_free(ssc);
+		ssc_p->ssc = NULL;
+	}
+
+	if (_gclk0 != NULL) {
+		clk_put(_gclk0);
+		_gclk0 = NULL;
+	}
+	if (_pll0 != NULL) {
+		clk_put(_pll0);
+		_pll0 = NULL;
+	}
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+	at32_free_pin(MCLK_PIN);
+#endif
+
+	platform_device_unregister(playpaq_snd_device);
+	playpaq_snd_device = NULL;
+}
+
+module_init(playpaq_asoc_init);
+module_exit(playpaq_asoc_exit);
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("ASoC machine driver for LRS PlayPaq");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
index 5cb93fd..9051865 100644
--- a/sound/soc/at91/Kconfig
+++ b/sound/soc/at91/Kconfig
@@ -1,6 +1,6 @@
 config SND_AT91_SOC
 	tristate "SoC Audio for the Atmel AT91 System-on-Chip"
-	depends on ARCH_AT91 && SND_SOC
+	depends on ARCH_AT91
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the AT91 SSC interface. You will also need
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
index ccac6bd..d47492b 100644
--- a/sound/soc/at91/at91-pcm.c
+++ b/sound/soc/at91/at91-pcm.c
@@ -318,7 +318,7 @@
 static u64 at91_pcm_dmamask = 0xffffffff;
 
 static int at91_pcm_new(struct snd_card *card,
-	struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+	struct snd_soc_dai *dai, struct snd_pcm *pcm)
 {
 	int ret = 0;
 
@@ -367,7 +367,7 @@
 
 #ifdef CONFIG_PM
 static int at91_pcm_suspend(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	struct snd_pcm_runtime *runtime = dai->runtime;
 	struct at91_runtime_data *prtd;
@@ -392,7 +392,7 @@
 }
 
 static int at91_pcm_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	struct snd_pcm_runtime *runtime = dai->runtime;
 	struct at91_runtime_data *prtd;
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
index bc35d00..c3625b6 100644
--- a/sound/soc/at91/at91-ssc.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -281,7 +281,7 @@
 /*
  * Record the SSC system clock rate.
  */
-static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	/*
@@ -303,7 +303,7 @@
 /*
  * Record the DAI format for use in hw_params().
  */
-static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
 	struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -315,7 +315,7 @@
 /*
  * Record SSC clock dividers for use in hw_params().
  */
-static int at91_ssc_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
 	int div_id, int div)
 {
 	struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -634,7 +634,7 @@
 
 #ifdef CONFIG_PM
 static int at91_ssc_suspend(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *cpu_dai)
+	struct snd_soc_dai *cpu_dai)
 {
 	struct at91_ssc_info *ssc_p;
 
@@ -662,7 +662,7 @@
 }
 
 static int at91_ssc_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *cpu_dai)
+	struct snd_soc_dai *cpu_dai)
 {
 	struct at91_ssc_info *ssc_p;
 
@@ -700,7 +700,7 @@
 #define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
 			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
-struct snd_soc_cpu_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
+struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
 	{	.name = "at91-ssc0",
 		.id = 0,
 		.type = SND_SOC_DAI_PCM,
diff --git a/sound/soc/at91/at91-ssc.h b/sound/soc/at91/at91-ssc.h
index b188f97..6b7bf38 100644
--- a/sound/soc/at91/at91-ssc.h
+++ b/sound/soc/at91/at91-ssc.h
@@ -21,7 +21,7 @@
 #define AT91SSC_TCMR_PERIOD	1 /* BCLK divider for transmit FS */
 #define AT91SSC_RCMR_PERIOD	2 /* BCLK divider for receive FS */
 
-extern struct snd_soc_cpu_dai at91_ssc_dai[];
+extern struct snd_soc_dai at91_ssc_dai[];
 
 #endif /* _AT91_SSC_H */
 
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
index 1347dcf..d532de9 100644
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ b/sound/soc/at91/eti_b1_wm8731.c
@@ -53,18 +53,18 @@
 static int eti_b1_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int ret;
 
 	/* cpu clock is the AT91 master clock sent to the SSC */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
 		60000000, SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* codec system clock is supplied by PCK1, set to 12MHz */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
 		12000000, SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -87,8 +87,8 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int ret;
 
 #ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
@@ -96,13 +96,13 @@
 	int cmr_div, period;
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
@@ -141,17 +141,17 @@
 	}
 
 	/* set the MCK divider for BCLK */
-	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
 	if (ret < 0)
 		return ret;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		/* set the BCLK divider for DACLRC */
-		ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
 						AT91SSC_TCMR_PERIOD, period);
 	} else {
 		/* set the BCLK divider for ADCLRC */
-		ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
 						AT91SSC_RCMR_PERIOD, period);
 	}
 	if (ret < 0)
@@ -163,13 +163,13 @@
 	 */
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
@@ -191,7 +191,7 @@
 	SND_SOC_DAPM_SPK("Ext Spk", NULL),
 };
 
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
 
 	/* speaker connected to LHPOUT */
 	{"Ext Spk", NULL, "LHPOUT"},
@@ -199,9 +199,6 @@
 	/* mic is connected to Mic Jack, with WM8731 Mic Bias */
 	{"MICIN", NULL, "Mic Bias"},
 	{"Mic Bias", NULL, "Int Mic"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 /*
@@ -209,30 +206,24 @@
  */
 static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
 {
-	int i;
-
 	DBG("eti_b1_wm8731_init() called\n");
 
 	/* Add specific widgets */
-	for(i = 0; i < ARRAY_SIZE(eti_b1_dapm_widgets); i++) {
-		snd_soc_dapm_new_control(codec, &eti_b1_dapm_widgets[i]);
-	}
+	snd_soc_dapm_new_controls(codec, eti_b1_dapm_widgets,
+				  ARRAY_SIZE(eti_b1_dapm_widgets));
 
 	/* Set up specific audio path interconnects */
-	for(i = 0; intercon[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, intercon[i][0],
-			intercon[i][1], intercon[i][2]);
-	}
+	snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon));
 
 	/* not connected */
-	snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
+	snd_soc_dapm_disable_pin(codec, "RLINEIN");
+	snd_soc_dapm_disable_pin(codec, "LLINEIN");
 
 	/* always connected */
-	snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+	snd_soc_dapm_enable_pin(codec, "Int Mic");
+	snd_soc_dapm_enable_pin(codec, "Ext Spk");
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
new file mode 100644
index 0000000..410a893
--- /dev/null
+++ b/sound/soc/au1x/Kconfig
@@ -0,0 +1,32 @@
+##
+## Au1200/Au1550 PSC + DBDMA
+##
+config SND_SOC_AU1XPSC
+	tristate "SoC Audio for Au1200/Au1250/Au1550"
+	depends on SOC_AU1200 || SOC_AU1550
+	help
+	  This option enables support for the Programmable Serial
+	  Controllers in AC97 and I2S mode, and the Descriptor-Based DMA
+	  Controller (DBDMA) as found on the Au1200/Au1250/Au1550 SoC.
+
+config SND_SOC_AU1XPSC_I2S
+	tristate
+
+config SND_SOC_AU1XPSC_AC97
+	tristate
+	select AC97_BUS
+	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_SAMPLE_PSC_AC97
+	tristate "Sample Au12x0/Au1550 PSC AC97 sound machine"
+	depends on SND_SOC_AU1XPSC
+	select SND_SOC_AU1XPSC_AC97
+	select SND_SOC_AC97_CODEC
+	help
+	  This is a sample AC97 sound machine for use in Au12x0/Au1550
+	  based systems which have audio on PSC1 (e.g. Db1200 demoboard).
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
new file mode 100644
index 0000000..6c6950b
--- /dev/null
+++ b/sound/soc/au1x/Makefile
@@ -0,0 +1,13 @@
+# Au1200/Au1550 PSC audio
+snd-soc-au1xpsc-dbdma-objs := dbdma2.o
+snd-soc-au1xpsc-i2s-objs := psc-i2s.o
+snd-soc-au1xpsc-ac97-objs := psc-ac97.o
+
+obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
+obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
+
+# Boards
+snd-soc-sample-ac97-objs := sample-ac97.o
+
+obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
new file mode 100644
index 0000000..1466d93
--- /dev/null
+++ b/sound/soc/au1x/dbdma2.c
@@ -0,0 +1,421 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * DMA glue for Au1x-PSC audio.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/*#define PCM_DEBUG*/
+
+#define MSG(x...)	printk(KERN_INFO "au1xpsc_pcm: " x)
+#ifdef PCM_DEBUG
+#define DBG		MSG
+#else
+#define DBG(x...)	do {} while (0)
+#endif
+
+struct au1xpsc_audio_dmadata {
+	/* DDMA control data */
+	unsigned int ddma_id;		/* DDMA direction ID for this PSC */
+	u32 ddma_chan;			/* DDMA context */
+
+	/* PCM context (for irq handlers) */
+	struct snd_pcm_substream *substream;
+	unsigned long curr_period;	/* current segment DDMA is working on */
+	unsigned long q_period;		/* queue period(s) */
+	unsigned long dma_area;		/* address of queued DMA area */
+	unsigned long dma_area_s;	/* start address of DMA area */
+	unsigned long pos;		/* current byte position being played */
+	unsigned long periods;		/* number of SG segments in total */
+	unsigned long period_bytes;	/* size in bytes of one SG segment */
+
+	/* runtime data */
+	int msbits;
+};
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_dmadata *au1xpsc_audio_pcmdma[2];
+
+/*
+ * These settings are somewhat okay, at least on my machine audio plays
+ * almost skip-free. Especially the 64kB buffer seems to help a LOT.
+ */
+#define AU1XPSC_PERIOD_MIN_BYTES	1024
+#define AU1XPSC_BUFFER_MIN_BYTES	65536
+
+#define AU1XPSC_PCM_FMTS					\
+	(SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_U8 |	\
+	 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |	\
+	 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |	\
+	 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |	\
+	 SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE |	\
+	 0)
+
+/* PCM hardware DMA capabilities - platform specific */
+static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
+	.info		  = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+			    SNDRV_PCM_INFO_INTERLEAVED,
+	.formats	  = AU1XPSC_PCM_FMTS,
+	.period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
+	.period_bytes_max = 4096 * 1024 - 1,
+	.periods_min	  = 2,
+	.periods_max	  = 4096,	/* 2 to as-much-as-you-like */
+	.buffer_bytes_max = 4096 * 1024 - 1,
+	.fifo_size	  = 16,		/* fifo entries of AC97/I2S PSC */
+};
+
+static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
+{
+	au1xxx_dbdma_put_source_flags(cd->ddma_chan,
+				(void *)phys_to_virt(cd->dma_area),
+				cd->period_bytes, DDMA_FLAGS_IE);
+
+	/* update next-to-queue period */
+	++cd->q_period;
+	cd->dma_area += cd->period_bytes;
+	if (cd->q_period >= cd->periods) {
+		cd->q_period = 0;
+		cd->dma_area = cd->dma_area_s;
+	}
+}
+
+static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd)
+{
+	au1xxx_dbdma_put_dest_flags(cd->ddma_chan,
+				(void *)phys_to_virt(cd->dma_area),
+				cd->period_bytes, DDMA_FLAGS_IE);
+
+	/* update next-to-queue period */
+	++cd->q_period;
+	cd->dma_area += cd->period_bytes;
+	if (cd->q_period >= cd->periods) {
+		cd->q_period = 0;
+		cd->dma_area = cd->dma_area_s;
+	}
+}
+
+static void au1x_pcm_dmatx_cb(int irq, void *dev_id)
+{
+	struct au1xpsc_audio_dmadata *cd = dev_id;
+
+	cd->pos += cd->period_bytes;
+	if (++cd->curr_period >= cd->periods) {
+		cd->pos = 0;
+		cd->curr_period = 0;
+	}
+	snd_pcm_period_elapsed(cd->substream);
+	au1x_pcm_queue_tx(cd);
+}
+
+static void au1x_pcm_dmarx_cb(int irq, void *dev_id)
+{
+	struct au1xpsc_audio_dmadata *cd = dev_id;
+
+	cd->pos += cd->period_bytes;
+	if (++cd->curr_period >= cd->periods) {
+		cd->pos = 0;
+		cd->curr_period = 0;
+	}
+	snd_pcm_period_elapsed(cd->substream);
+	au1x_pcm_queue_rx(cd);
+}
+
+static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd)
+{
+	if (pcd->ddma_chan) {
+		au1xxx_dbdma_stop(pcd->ddma_chan);
+		au1xxx_dbdma_reset(pcd->ddma_chan);
+		au1xxx_dbdma_chan_free(pcd->ddma_chan);
+		pcd->ddma_chan = 0;
+		pcd->msbits = 0;
+	}
+}
+
+/* in case of missing DMA ring or changed TX-source / RX-dest bit widths,
+ * allocate (or reallocate) a 2-descriptor DMA ring with bit depth according
+ * to ALSA-supplied sample depth.  This is due to limitations in the dbdma api
+ * (cannot adjust source/dest widths of already allocated descriptor ring).
+ */
+static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
+				 int stype, int msbits)
+{
+	/* DMA only in 8/16/32 bit widths */
+	if (msbits == 24)
+		msbits = 32;
+
+	/* check current config: correct bits and descriptors allocated? */
+	if ((pcd->ddma_chan) && (msbits == pcd->msbits))
+		goto out;	/* all ok! */
+
+	au1x_pcm_dbdma_free(pcd);
+
+	if (stype == PCM_RX)
+		pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
+					DSCR_CMD0_ALWAYS,
+					au1x_pcm_dmarx_cb, (void *)pcd);
+	else
+		pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS,
+					pcd->ddma_id,
+					au1x_pcm_dmatx_cb, (void *)pcd);
+
+	if (!pcd->ddma_chan)
+		return -ENOMEM;;
+
+	au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits);
+	au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2);
+
+	pcd->msbits = msbits;
+
+	au1xxx_dbdma_stop(pcd->ddma_chan);
+	au1xxx_dbdma_reset(pcd->ddma_chan);
+
+out:
+	return 0;
+}
+
+static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct au1xpsc_audio_dmadata *pcd;
+	int stype, ret;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		goto out;
+
+	stype = SUBSTREAM_TYPE(substream);
+	pcd = au1xpsc_audio_pcmdma[stype];
+
+	DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
+	    "runtime->min_align %d\n",
+		(unsigned long)runtime->dma_area,
+		(unsigned long)runtime->dma_addr, runtime->dma_bytes,
+		runtime->min_align);
+
+	DBG("bits %d  frags %d  frag_bytes %d  is_rx %d\n", params->msbits,
+		params_periods(params), params_period_bytes(params), stype);
+
+	ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits);
+	if (ret) {
+		MSG("DDMA channel (re)alloc failed!\n");
+		goto out;
+	}
+
+	pcd->substream = substream;
+	pcd->period_bytes = params_period_bytes(params);
+	pcd->periods = params_periods(params);
+	pcd->dma_area_s = pcd->dma_area = (unsigned long)runtime->dma_addr;
+	pcd->q_period = 0;
+	pcd->curr_period = 0;
+	pcd->pos = 0;
+
+	ret = 0;
+out:
+	return ret;
+}
+
+static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct au1xpsc_audio_dmadata *pcd =
+			au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)];
+
+	au1xxx_dbdma_reset(pcd->ddma_chan);
+
+	if (SUBSTREAM_TYPE(substream) == PCM_RX) {
+		au1x_pcm_queue_rx(pcd);
+		au1x_pcm_queue_rx(pcd);
+	} else {
+		au1x_pcm_queue_tx(pcd);
+		au1x_pcm_queue_tx(pcd);
+	}
+
+	return 0;
+}
+
+static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	u32 c = au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->ddma_chan;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		au1xxx_dbdma_start(c);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		au1xxx_dbdma_stop(c);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static snd_pcm_uframes_t
+au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	return bytes_to_frames(substream->runtime,
+		au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->pos);
+}
+
+static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
+{
+	snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
+	return 0;
+}
+
+static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
+{
+	au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]);
+	return 0;
+}
+
+struct snd_pcm_ops au1xpsc_pcm_ops = {
+	.open		= au1xpsc_pcm_open,
+	.close		= au1xpsc_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= au1xpsc_pcm_hw_params,
+	.hw_free	= au1xpsc_pcm_hw_free,
+	.prepare	= au1xpsc_pcm_prepare,
+	.trigger	= au1xpsc_pcm_trigger,
+	.pointer	= au1xpsc_pcm_pointer,
+};
+
+static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int au1xpsc_pcm_new(struct snd_card *card,
+			   struct snd_soc_dai *dai,
+			   struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+		card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
+
+	return 0;
+}
+
+static int au1xpsc_pcm_probe(struct platform_device *pdev)
+{
+	struct resource *r;
+	int ret;
+
+	if (au1xpsc_audio_pcmdma[PCM_TX] || au1xpsc_audio_pcmdma[PCM_RX])
+		return -EBUSY;
+
+	/* TX DMA */
+	au1xpsc_audio_pcmdma[PCM_TX]
+		= kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+	if (!au1xpsc_audio_pcmdma[PCM_TX])
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out1;
+	}
+	(au1xpsc_audio_pcmdma[PCM_TX])->ddma_id = r->start;
+
+	/* RX DMA */
+	au1xpsc_audio_pcmdma[PCM_RX]
+		= kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+	if (!au1xpsc_audio_pcmdma[PCM_RX])
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!r) {
+		ret = -ENODEV;
+		goto out2;
+	}
+	(au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
+
+	return 0;
+
+out2:
+	kfree(au1xpsc_audio_pcmdma[PCM_RX]);
+	au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+out1:
+	kfree(au1xpsc_audio_pcmdma[PCM_TX]);
+	au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+	return ret;
+}
+
+static int au1xpsc_pcm_remove(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		if (au1xpsc_audio_pcmdma[i]) {
+			au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
+			kfree(au1xpsc_audio_pcmdma[i]);
+			au1xpsc_audio_pcmdma[i] = NULL;
+		}
+	}
+
+	return 0;
+}
+
+/* au1xpsc audio platform */
+struct snd_soc_platform au1xpsc_soc_platform = {
+	.name		= "au1xpsc-pcm-dbdma",
+	.probe		= au1xpsc_pcm_probe,
+	.remove		= au1xpsc_pcm_remove,
+	.pcm_ops 	= &au1xpsc_pcm_ops,
+	.pcm_new	= au1xpsc_pcm_new,
+	.pcm_free	= au1xpsc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
+
+static int __init au1xpsc_audio_dbdma_init(void)
+{
+	au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+	au1xpsc_audio_pcmdma[PCM_RX] = NULL;
+	return 0;
+}
+
+static void __exit au1xpsc_audio_dbdma_exit(void)
+{
+}
+
+module_init(au1xpsc_audio_dbdma_init);
+module_exit(au1xpsc_audio_dbdma_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
new file mode 100644
index 0000000..57facba
--- /dev/null
+++ b/sound/soc/au1x/psc-ac97.c
@@ -0,0 +1,387 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Au1xxx-PSC AC97 glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+#define AC97_DIR	\
+	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AC97_RATES	\
+	SNDRV_PCM_RATE_8000_48000
+
+#define AC97_FMTS	\
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
+
+#define AC97PCR_START(stype)	\
+	((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
+#define AC97PCR_STOP(stype)	\
+	((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
+#define AC97PCR_CLRFIFO(stype)	\
+	((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
+
+/* AC97 controller reads codec register */
+static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
+					unsigned short reg)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	unsigned short data, tmo;
+
+	au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
+	au_sync();
+
+	tmo = 1000;
+	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+		udelay(2);
+
+	if (!tmo)
+		data = 0xffff;
+	else
+		data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+
+	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+	au_sync();
+
+	return data;
+}
+
+/* AC97 controller writes to codec register */
+static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+				unsigned short val)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	unsigned int tmo;
+
+	au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
+	au_sync();
+	tmo = 1000;
+	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+		au_sync();
+
+	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+	au_sync();
+}
+
+/* AC97 controller asserts a warm reset */
+static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+
+	au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
+	au_sync();
+	msleep(10);
+	au_writel(0, AC97_RST(pscdata));
+	au_sync();
+}
+
+static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	int i;
+
+	/* disable PSC during cold reset */
+	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
+	au_sync();
+
+	/* issue cold reset */
+	au_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
+	au_sync();
+	msleep(500);
+	au_writel(0, AC97_RST(pscdata));
+	au_sync();
+
+	/* enable PSC */
+	au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+	au_sync();
+
+	/* wait for PSC to indicate it's ready */
+	i = 100000;
+	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
+		au_sync();
+
+	if (i == 0) {
+		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
+		return;
+	}
+
+	/* enable the ac97 function */
+	au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+	au_sync();
+
+	/* wait for AC97 core to become ready */
+	i = 100000;
+	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
+		au_sync();
+	if (i == 0)
+		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read		= au1xpsc_ac97_read,
+	.write		= au1xpsc_ac97_write,
+	.reset		= au1xpsc_ac97_cold_reset,
+	.warm_reset	= au1xpsc_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	unsigned long r, stat;
+	int chans, stype = SUBSTREAM_TYPE(substream);
+
+	chans = params_channels(params);
+
+	r = au_readl(AC97_CFG(pscdata));
+	stat = au_readl(AC97_STAT(pscdata));
+
+	/* already active? */
+	if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
+		/* reject parameters not currently set up */
+		if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
+		    (pscdata->rate != params_rate(params)))
+			return -EINVAL;
+	} else {
+		/* disable AC97 device controller first */
+		au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+		au_sync();
+
+		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
+		r &= ~PSC_AC97CFG_LEN_MASK;
+		r |= PSC_AC97CFG_SET_LEN(params->msbits);
+
+		/* channels: enable slots for front L/R channel */
+		if (stype == PCM_TX) {
+			r &= ~PSC_AC97CFG_TXSLOT_MASK;
+			r |= PSC_AC97CFG_TXSLOT_ENA(3);
+			r |= PSC_AC97CFG_TXSLOT_ENA(4);
+		} else {
+			r &= ~PSC_AC97CFG_RXSLOT_MASK;
+			r |= PSC_AC97CFG_RXSLOT_ENA(3);
+			r |= PSC_AC97CFG_RXSLOT_ENA(4);
+		}
+
+		/* finally enable the AC97 controller again */
+		au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+		au_sync();
+
+		pscdata->cfg = r;
+		pscdata->rate = params_rate(params);
+	}
+
+	return 0;
+}
+
+static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
+				int cmd)
+{
+	/* FIXME */
+	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+	int ret, stype = SUBSTREAM_TYPE(substream);
+
+	ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
+		au_sync();
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
+		au_sync();
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int au1xpsc_ac97_probe(struct platform_device *pdev,
+			      struct snd_soc_dai *dai)
+{
+	int ret;
+	struct resource *r;
+	unsigned long sel;
+
+	if (au1xpsc_ac97_workdata)
+		return -EBUSY;
+
+	au1xpsc_ac97_workdata =
+		kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+	if (!au1xpsc_ac97_workdata)
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out0;
+	}
+
+	ret = -EBUSY;
+	au1xpsc_ac97_workdata->ioarea =
+		request_mem_region(r->start, r->end - r->start + 1,
+					"au1xpsc_ac97");
+	if (!au1xpsc_ac97_workdata->ioarea)
+		goto out0;
+
+	au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
+	if (!au1xpsc_ac97_workdata->mmio)
+		goto out1;
+
+	/* configuration: max dma trigger threshold, enable ac97 */
+	 au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
+				      PSC_AC97CFG_TT_FIFO8 |
+				      PSC_AC97CFG_DE_ENABLE;
+
+	/* preserve PSC clock source set up by platform (dev.platform_data
+	 * is already occupied by soc layer)
+	 */
+	sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
+	au_sync();
+	/* next up: cold reset.  Dont check for PSC-ready now since
+	 * there may not be any codec clock yet.
+	 */
+
+	return 0;
+
+out1:
+	release_resource(au1xpsc_ac97_workdata->ioarea);
+	kfree(au1xpsc_ac97_workdata->ioarea);
+out0:
+	kfree(au1xpsc_ac97_workdata);
+	au1xpsc_ac97_workdata = NULL;
+	return ret;
+}
+
+static void au1xpsc_ac97_remove(struct platform_device *pdev,
+				struct snd_soc_dai *dai)
+{
+	/* disable PSC completely */
+	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+	au_sync();
+
+	iounmap(au1xpsc_ac97_workdata->mmio);
+	release_resource(au1xpsc_ac97_workdata->ioarea);
+	kfree(au1xpsc_ac97_workdata->ioarea);
+	kfree(au1xpsc_ac97_workdata);
+	au1xpsc_ac97_workdata = NULL;
+}
+
+static int au1xpsc_ac97_suspend(struct platform_device *pdev,
+				struct snd_soc_dai *dai)
+{
+	/* save interesting registers and disable PSC */
+	au1xpsc_ac97_workdata->pm[0] =
+			au_readl(PSC_SEL(au1xpsc_ac97_workdata));
+
+	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
+	au_sync();
+
+	return 0;
+}
+
+static int au1xpsc_ac97_resume(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
+{
+	/* restore PSC clock config */
+	au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
+			PSC_SEL(au1xpsc_ac97_workdata));
+	au_sync();
+
+	/* after this point the ac97 core will cold-reset the codec.
+	 * During cold-reset the PSC is reinitialized and the last
+	 * configuration set up in hw_params() is restored.
+	 */
+	return 0;
+}
+
+struct snd_soc_dai au1xpsc_ac97_dai = {
+	.name			= "au1xpsc_ac97",
+	.type			= SND_SOC_DAI_AC97,
+	.probe			= au1xpsc_ac97_probe,
+	.remove			= au1xpsc_ac97_remove,
+	.suspend		= au1xpsc_ac97_suspend,
+	.resume			= au1xpsc_ac97_resume,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.ops = {
+		.trigger	= au1xpsc_ac97_trigger,
+		.hw_params	= au1xpsc_ac97_hw_params,
+	},
+};
+EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
+
+static int __init au1xpsc_ac97_init(void)
+{
+	au1xpsc_ac97_workdata = NULL;
+	return 0;
+}
+
+static void __exit au1xpsc_ac97_exit(void)
+{
+}
+
+module_init(au1xpsc_ac97_init);
+module_exit(au1xpsc_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
new file mode 100644
index 0000000..ba4b5c1
--- /dev/null
+++ b/sound/soc/au1x/psc-i2s.c
@@ -0,0 +1,414 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Au1xxx-PSC I2S glue.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ * NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "psc.h"
+
+/* supported I2S DAI hardware formats */
+#define AU1XPSC_I2S_DAIFMT \
+	(SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J |	\
+	 SND_SOC_DAIFMT_NB_NF)
+
+/* supported I2S direction */
+#define AU1XPSC_I2S_DIR \
+	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AU1XPSC_I2S_RATES \
+	SNDRV_PCM_RATE_8000_192000
+
+#define AU1XPSC_I2S_FMTS \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+#define I2SSTAT_BUSY(stype)	\
+	((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
+#define I2SPCR_START(stype)	\
+	((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
+#define I2SPCR_STOP(stype)	\
+	((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
+#define I2SPCR_CLRFIFO(stype)	\
+	((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
+
+
+/* instance data. There can be only one, MacLeod!!!! */
+static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
+
+static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+			       unsigned int fmt)
+{
+	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+	unsigned long ct;
+	int ret;
+
+	ret = -EINVAL;
+
+	ct = pscdata->cfg;
+
+	ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ);	/* left-justified */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ct |= PSC_I2SCFG_XM;	/* enable I2S mode */
+		break;
+	case SND_SOC_DAIFMT_MSB:
+		break;
+	case SND_SOC_DAIFMT_LSB:
+		ct |= PSC_I2SCFG_MLJ;	/* LSB (right-) justified */
+		break;
+	default:
+		goto out;
+	}
+
+	ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI);		/* IB-IF */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ct |= PSC_I2SCFG_BI;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		ct |= PSC_I2SCFG_WI;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		break;
+	default:
+		goto out;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:	/* CODEC master */
+		ct |= PSC_I2SCFG_MS;	/* PSC I2S slave mode */
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:	/* CODEC slave */
+		ct &= ~PSC_I2SCFG_MS;	/* PSC I2S Master mode */
+		break;
+	default:
+		goto out;
+	}
+
+	pscdata->cfg = ct;
+	ret = 0;
+out:
+	return ret;
+}
+
+static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+
+	int cfgbits;
+	unsigned long stat;
+
+	/* check if the PSC is already streaming data */
+	stat = au_readl(I2S_STAT(pscdata));
+	if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) {
+		/* reject parameters not currently set up in hardware */
+		cfgbits = au_readl(I2S_CFG(pscdata));
+		if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
+		    (params_rate(params) != pscdata->rate))
+			return -EINVAL;
+	} else {
+		/* set sample bitdepth */
+		pscdata->cfg &= ~(0x1f << 4);
+		pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits);
+		/* remember current rate for other stream */
+		pscdata->rate = params_rate(params);
+	}
+	return 0;
+}
+
+/* Configure PSC late:  on my devel systems the codec  is I2S master and
+ * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit.  ASoC
+ * uses aggressive PM and  switches the codec off  when it is not in use
+ * which also means the PSC unit doesn't get any clocks and is therefore
+ * dead. That's why this chunk here gets called from the trigger callback
+ * because I can be reasonably certain the codec is driving the clocks.
+ */
+static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
+{
+	unsigned long tmo;
+
+	/* bring PSC out of sleep, and configure I2S unit */
+	au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
+	au_sync();
+
+	tmo = 1000000;
+	while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
+		tmo--;
+
+	if (!tmo)
+		goto psc_err;
+
+	au_writel(0, I2S_CFG(pscdata));
+	au_sync();
+	au_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
+	au_sync();
+
+	/* wait for I2S controller to become ready */
+	tmo = 1000000;
+	while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
+		tmo--;
+
+	if (tmo)
+		return 0;
+
+psc_err:
+	au_writel(0, I2S_CFG(pscdata));
+	au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+	au_sync();
+	return -ETIMEDOUT;
+}
+
+static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
+{
+	unsigned long tmo, stat;
+	int ret;
+
+	ret = 0;
+
+	/* if both TX and RX are idle, configure the PSC  */
+	stat = au_readl(I2S_STAT(pscdata));
+	if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
+		ret = au1xpsc_i2s_configure(pscdata);
+		if (ret)
+			goto out;
+	}
+
+	au_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
+	au_sync();
+	au_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
+	au_sync();
+
+	/* wait for start confirmation */
+	tmo = 1000000;
+	while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+		tmo--;
+
+	if (!tmo) {
+		au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+		au_sync();
+		ret = -ETIMEDOUT;
+	}
+out:
+	return ret;
+}
+
+static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
+{
+	unsigned long tmo, stat;
+
+	au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
+	au_sync();
+
+	/* wait for stop confirmation */
+	tmo = 1000000;
+	while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
+		tmo--;
+
+	/* if both TX and RX are idle, disable PSC */
+	stat = au_readl(I2S_STAT(pscdata));
+	if (!(stat & (PSC_I2SSTAT_RB | PSC_I2SSTAT_RB))) {
+		au_writel(0, I2S_CFG(pscdata));
+		au_sync();
+		au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
+		au_sync();
+	}
+	return 0;
+}
+
+static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+	int ret, stype = SUBSTREAM_TYPE(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		ret = au1xpsc_i2s_start(pscdata, stype);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		ret = au1xpsc_i2s_stop(pscdata, stype);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int au1xpsc_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
+{
+	struct resource *r;
+	unsigned long sel;
+	int ret;
+
+	if (au1xpsc_i2s_workdata)
+		return -EBUSY;
+
+	au1xpsc_i2s_workdata =
+		kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+	if (!au1xpsc_i2s_workdata)
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out0;
+	}
+
+	ret = -EBUSY;
+	au1xpsc_i2s_workdata->ioarea =
+		request_mem_region(r->start, r->end - r->start + 1,
+					"au1xpsc_i2s");
+	if (!au1xpsc_i2s_workdata->ioarea)
+		goto out0;
+
+	au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
+	if (!au1xpsc_i2s_workdata->mmio)
+		goto out1;
+
+	/* preserve PSC clock source set up by platform (dev.platform_data
+	 * is already occupied by soc layer)
+	 */
+	sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
+	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+	au_sync();
+
+	/* preconfigure: set max rx/tx fifo depths */
+	au1xpsc_i2s_workdata->cfg |=
+			PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
+
+	/* don't wait for I2S core to become ready now; clocks may not
+	 * be running yet; depending on clock input for PSC a wait might
+	 * time out.
+	 */
+
+	return 0;
+
+out1:
+	release_resource(au1xpsc_i2s_workdata->ioarea);
+	kfree(au1xpsc_i2s_workdata->ioarea);
+out0:
+	kfree(au1xpsc_i2s_workdata);
+	au1xpsc_i2s_workdata = NULL;
+	return ret;
+}
+
+static void au1xpsc_i2s_remove(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
+{
+	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+
+	iounmap(au1xpsc_i2s_workdata->mmio);
+	release_resource(au1xpsc_i2s_workdata->ioarea);
+	kfree(au1xpsc_i2s_workdata->ioarea);
+	kfree(au1xpsc_i2s_workdata);
+	au1xpsc_i2s_workdata = NULL;
+}
+
+static int au1xpsc_i2s_suspend(struct platform_device *pdev,
+			       struct snd_soc_dai *cpu_dai)
+{
+	/* save interesting register and disable PSC */
+	au1xpsc_i2s_workdata->pm[0] =
+		au_readl(PSC_SEL(au1xpsc_i2s_workdata));
+
+	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+
+	return 0;
+}
+
+static int au1xpsc_i2s_resume(struct platform_device *pdev,
+			      struct snd_soc_dai *cpu_dai)
+{
+	/* select I2S mode and PSC clock */
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
+	au_sync();
+	au_writel(au1xpsc_i2s_workdata->pm[0],
+			PSC_SEL(au1xpsc_i2s_workdata));
+	au_sync();
+
+	return 0;
+}
+
+struct snd_soc_dai au1xpsc_i2s_dai = {
+	.name			= "au1xpsc_i2s",
+	.type			= SND_SOC_DAI_I2S,
+	.probe			= au1xpsc_i2s_probe,
+	.remove			= au1xpsc_i2s_remove,
+	.suspend		= au1xpsc_i2s_suspend,
+	.resume			= au1xpsc_i2s_resume,
+	.playback = {
+		.rates		= AU1XPSC_I2S_RATES,
+		.formats	= AU1XPSC_I2S_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,	/* 2 without external help */
+	},
+	.capture = {
+		.rates		= AU1XPSC_I2S_RATES,
+		.formats	= AU1XPSC_I2S_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,	/* 2 without external help */
+	},
+	.ops = {
+		.trigger	= au1xpsc_i2s_trigger,
+		.hw_params	= au1xpsc_i2s_hw_params,
+	},
+	.dai_ops = {
+		.set_fmt	= au1xpsc_i2s_set_fmt,
+	},
+};
+EXPORT_SYMBOL(au1xpsc_i2s_dai);
+
+static int __init au1xpsc_i2s_init(void)
+{
+	au1xpsc_i2s_workdata = NULL;
+	return 0;
+}
+
+static void __exit au1xpsc_i2s_exit(void)
+{
+}
+
+module_init(au1xpsc_i2s_init);
+module_exit(au1xpsc_i2s_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
new file mode 100644
index 0000000..8fdb1a0
--- /dev/null
+++ b/sound/soc/au1x/psc.h
@@ -0,0 +1,53 @@
+/*
+ * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ *
+ * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * NOTE: all of these drivers can only work with a SINGLE instance
+ *	 of a PSC. Multiple independent audio devices are impossible
+ *	 with ASoC v1.
+ */
+
+#ifndef _AU1X_PCM_H
+#define _AU1X_PCM_H
+
+extern struct snd_soc_dai au1xpsc_ac97_dai;
+extern struct snd_soc_dai au1xpsc_i2s_dai;
+extern struct snd_soc_platform au1xpsc_soc_platform;
+extern struct snd_ac97_bus_ops soc_ac97_ops;
+
+struct au1xpsc_audio_data {
+	void __iomem *mmio;
+
+	unsigned long cfg;
+	unsigned long rate;
+
+	unsigned long pm[2];
+	struct resource *ioarea;
+};
+
+#define PCM_TX	0
+#define PCM_RX	1
+
+#define SUBSTREAM_TYPE(substream) \
+	((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
+
+/* easy access macros */
+#define PSC_CTRL(x)	((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET)
+#define PSC_SEL(x)	((unsigned long)((x)->mmio) + PSC_SEL_OFFSET)
+#define I2S_STAT(x)	((unsigned long)((x)->mmio) + PSC_I2SSTAT_OFFSET)
+#define I2S_CFG(x)	((unsigned long)((x)->mmio) + PSC_I2SCFG_OFFSET)
+#define I2S_PCR(x)	((unsigned long)((x)->mmio) + PSC_I2SPCR_OFFSET)
+#define AC97_CFG(x)	((unsigned long)((x)->mmio) + PSC_AC97CFG_OFFSET)
+#define AC97_CDC(x)	((unsigned long)((x)->mmio) + PSC_AC97CDC_OFFSET)
+#define AC97_EVNT(x)	((unsigned long)((x)->mmio) + PSC_AC97EVNT_OFFSET)
+#define AC97_PCR(x)	((unsigned long)((x)->mmio) + PSC_AC97PCR_OFFSET)
+#define AC97_RST(x)	((unsigned long)((x)->mmio) + PSC_AC97RST_OFFSET)
+#define AC97_STAT(x)	((unsigned long)((x)->mmio) + PSC_AC97STAT_OFFSET)
+
+#endif
diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c
new file mode 100644
index 0000000..f75ae7f
--- /dev/null
+++ b/sound/soc/au1x/sample-ac97.c
@@ -0,0 +1,144 @@
+/*
+ * Sample Au12x0/Au1550 PSC AC97 sound machine.
+ *
+ * Copyright (c) 2007-2008 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms outlined in the file COPYING at the root of this
+ *  source archive.
+ *
+ * This is a very generic AC97 sound machine driver for boards which
+ * have (AC97) audio at PSC1 (e.g. DB1200 demoboards).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+
+#include "../codecs/ac97.h"
+#include "psc.h"
+
+static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_sync(codec);
+	return 0;
+}
+
+static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
+	.name		= "AC97",
+	.stream_name	= "AC97 HiFi",
+	.cpu_dai	= &au1xpsc_ac97_dai,	/* see psc-ac97.c */
+	.codec_dai	= &ac97_dai,		/* see codecs/ac97.c */
+	.init		= au1xpsc_sample_ac97_init,
+	.ops		= NULL,
+};
+
+static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
+	.name		= "Au1xxx PSC AC97 Audio",
+	.dai_link	= &au1xpsc_sample_ac97_dai,
+	.num_links	= 1,
+};
+
+static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
+	.machine	= &au1xpsc_sample_ac97_machine,
+	.platform	= &au1xpsc_soc_platform, /* see dbdma2.c */
+	.codec_dev	= &soc_codec_dev_ac97,
+};
+
+static struct resource au1xpsc_psc1_res[] = {
+	[0] = {
+		.start	= CPHYSADDR(PSC1_BASE_ADDR),
+		.end	= CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+#ifdef CONFIG_SOC_AU1200
+		.start	= AU1200_PSC1_INT,
+		.end	= AU1200_PSC1_INT,
+#elif defined(CONFIG_SOC_AU1550)
+		.start	= AU1550_PSC1_INT,
+		.end	= AU1550_PSC1_INT,
+#endif
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.start	= DSCR_CMD0_PSC1_TX,
+		.end	= DSCR_CMD0_PSC1_TX,
+		.flags	= IORESOURCE_DMA,
+	},
+	[3] = {
+		.start	= DSCR_CMD0_PSC1_RX,
+		.end	= DSCR_CMD0_PSC1_RX,
+		.flags	= IORESOURCE_DMA,
+	},
+};
+
+static struct platform_device *au1xpsc_sample_ac97_dev;
+
+static int __init au1xpsc_sample_ac97_load(void)
+{
+	int ret;
+
+#ifdef CONFIG_SOC_AU1200
+	unsigned long io;
+
+	/* modify sys_pinfunc for AC97 on PSC1 */
+	io = au_readl(SYS_PINFUNC);
+	io |= SYS_PINFUNC_P1C;
+	io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B);
+	au_writel(io, SYS_PINFUNC);
+	au_sync();
+#endif
+
+	ret = -ENOMEM;
+
+	/* setup PSC clock source for AC97 part: external clock provided
+	 * by codec.  The psc-ac97.c driver depends on this setting!
+	 */
+	au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET);
+	au_sync();
+
+	au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1);
+	if (!au1xpsc_sample_ac97_dev)
+		goto out;
+
+	au1xpsc_sample_ac97_dev->resource =
+		kmemdup(au1xpsc_psc1_res, sizeof(struct resource) *
+			ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL);
+	au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res);
+	au1xpsc_sample_ac97_dev->id = 1;
+
+	platform_set_drvdata(au1xpsc_sample_ac97_dev,
+			     &au1xpsc_sample_ac97_devdata);
+	au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev;
+	ret = platform_device_add(au1xpsc_sample_ac97_dev);
+
+	if (ret) {
+		platform_device_put(au1xpsc_sample_ac97_dev);
+		au1xpsc_sample_ac97_dev = NULL;
+	}
+
+out:
+	return ret;
+}
+
+static void __exit au1xpsc_sample_ac97_exit(void)
+{
+	platform_device_unregister(au1xpsc_sample_ac97_dev);
+}
+
+module_init(au1xpsc_sample_ac97_load);
+module_exit(au1xpsc_sample_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3903ab7..1db04a2 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,31 +1,37 @@
 config SND_SOC_AC97_CODEC
 	tristate
-	depends on SND_SOC
+	select SND_AC97_CODEC
+
+config SND_SOC_AK4535
+	tristate
+
+config SND_SOC_UDA1380
+        tristate
+
+config SND_SOC_WM8510
+	tristate
 
 config SND_SOC_WM8731
 	tristate
-	depends on SND_SOC
 
 config SND_SOC_WM8750
 	tristate
-	depends on SND_SOC
 
 config SND_SOC_WM8753
 	tristate
-	depends on SND_SOC
+
+config SND_SOC_WM8990
+	tristate
 
 config SND_SOC_WM9712
 	tristate
-	depends on SND_SOC
 
 config SND_SOC_WM9713
 	tristate
-	depends on SND_SOC
 
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
 	tristate
-	depends on SND_SOC
 
 # Cirrus Logic CS4270 Codec Hardware Mute Support
 # Select if you have external muting circuitry attached to your CS4270.
@@ -43,4 +49,4 @@
 
 config SND_SOC_TLV320AIC3X
 	tristate
-	depends on SND_SOC && I2C
+	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 4e1314c..d7b97ab 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,16 +1,24 @@
 snd-soc-ac97-objs := ac97.o
+snd-soc-ak4535-objs := ak4535.o
+snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8990-objs := wm8990.o
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8990)	+= snd-soc-wm8990.o
 obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_WM9713)	+= snd-soc-wm9713.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 2a1ffe3..61fd96c 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -10,9 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    17th Oct 2005   Initial version.
- *
  * Generic AC97 support.
  */
 
@@ -24,6 +21,7 @@
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 #include <sound/soc.h>
+#include "ac97.h"
 
 #define AC97_VERSION "0.6"
 
@@ -43,7 +41,7 @@
 		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
 		SNDRV_PCM_RATE_48000)
 
-struct snd_soc_codec_dai ac97_dai = {
+struct snd_soc_dai ac97_dai = {
 	.name = "AC97 HiFi",
 	.type = SND_SOC_DAI_AC97,
 	.playback = {
@@ -146,9 +144,34 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int ac97_soc_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_ac97_suspend(socdev->codec->ac97);
+
+	return 0;
+}
+
+static int ac97_soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_ac97_resume(socdev->codec->ac97);
+
+	return 0;
+}
+#else
+#define ac97_soc_suspend NULL
+#define ac97_soc_resume NULL
+#endif
+
 struct snd_soc_codec_device soc_codec_dev_ac97 = {
 	.probe = 	ac97_soc_probe,
 	.remove = 	ac97_soc_remove,
+	.suspend =	ac97_soc_suspend,
+	.resume =	ac97_soc_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
 
diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h
index 2bf6d69..281aa42 100644
--- a/sound/soc/codecs/ac97.h
+++ b/sound/soc/codecs/ac97.h
@@ -14,6 +14,6 @@
 #define __LINUX_SND_SOC_AC97_H
 
 extern struct snd_soc_codec_device soc_codec_dev_ac97;
-extern struct snd_soc_codec_dai ac97_dai;
+extern struct snd_soc_dai ac97_dai;
 
 #endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
new file mode 100644
index 0000000..b26003c
--- /dev/null
+++ b/sound/soc/codecs/ak4535.c
@@ -0,0 +1,696 @@
+/*
+ * ak4535.c  --  AK4535 ALSA Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ak4535.h"
+
+#define AUDIO_NAME "ak4535"
+#define AK4535_VERSION "0.3"
+
+struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+/* codec private data */
+struct ak4535_priv {
+	unsigned int sysclk;
+};
+
+/*
+ * ak4535 register cache
+ */
+static const u16 ak4535_reg[AK4535_CACHEREGNUM] = {
+    0x0000, 0x0080, 0x0000, 0x0003,
+    0x0002, 0x0000, 0x0011, 0x0001,
+    0x0000, 0x0040, 0x0036, 0x0010,
+    0x0000, 0x0000, 0x0057, 0x0000,
+};
+
+/*
+ * read ak4535 register cache
+ */
+static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= AK4535_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u8 data;
+	data = reg;
+
+	if (codec->hw_write(codec->control_data, &data, 1) != 1)
+		return -EIO;
+
+	if (codec->hw_read(codec->control_data, &data, 1) != 1)
+		return -EIO;
+
+	return data;
+};
+
+/*
+ * write ak4535 register cache
+ */
+static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= AK4535_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the AK4535 register space
+ */
+static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D8 AK4535 register offset
+	 *   D7...D0 register data
+	 */
+	data[0] = reg & 0xff;
+	data[1] = value & 0xff;
+
+	ak4535_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+static int ak4535_sync(struct snd_soc_codec *codec)
+{
+	u16 *cache = codec->reg_cache;
+	int i, r = 0;
+
+	for (i = 0; i < AK4535_CACHEREGNUM; i++)
+		r |= ak4535_write(codec, i, cache[i]);
+
+	return r;
+};
+
+static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
+static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
+static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
+static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
+static const char *ak4535_mic_select[] = {"Internal", "External"};
+
+static const struct soc_enum ak4535_enum[] = {
+	SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
+	SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
+	SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
+	SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
+	SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
+};
+
+static const struct snd_kcontrol_new ak4535_snd_controls[] = {
+	SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
+	SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
+	SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
+	SOC_ENUM("Headphone Output", ak4535_enum[2]),
+	SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
+	SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
+	SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
+	SOC_ENUM("Mic Select", ak4535_enum[4]),
+	SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
+	SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
+	SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
+	SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
+	SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
+	SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
+	SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
+	SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
+	SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
+	SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
+	SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
+};
+
+/* add non dapm controls */
+static int ak4535_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+			snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Mono 1 Mixer */
+static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
+	SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
+};
+
+/* Stereo Mixer */
+static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
+	SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
+	SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
+};
+
+/* Input Mixer */
+static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
+	SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
+};
+
+/* Input mux */
+static const struct snd_kcontrol_new ak4535_input_mux_control =
+	SOC_DAPM_ENUM("Input Select", ak4535_enum[4]);
+
+/* HP L switch */
+static const struct snd_kcontrol_new ak4535_hpl_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
+
+/* HP R switch */
+static const struct snd_kcontrol_new ak4535_hpr_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
+
+/* mono 2 switch */
+static const struct snd_kcontrol_new ak4535_mono2_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
+
+/* Line out switch */
+static const struct snd_kcontrol_new ak4535_line_control =
+	SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
+
+/* ak4535 dapm widgets */
+static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
+	SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
+		&ak4535_stereo_mixer_controls[0],
+		ARRAY_SIZE(ak4535_stereo_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
+		&ak4535_mono1_mixer_controls[0],
+		ARRAY_SIZE(ak4535_mono1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
+		&ak4535_input_mixer_controls[0],
+		ARRAY_SIZE(ak4535_input_mixer_controls)),
+	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+		&ak4535_input_mux_control),
+	SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
+	SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_mono2_control),
+	/* speaker powersave bit */
+	SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_line_control),
+	SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_hpl_control),
+	SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
+		&ak4535_hpr_control),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("ROUT"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+	SND_SOC_DAPM_OUTPUT("SPP"),
+	SND_SOC_DAPM_OUTPUT("SPN"),
+	SND_SOC_DAPM_OUTPUT("MOUT1"),
+	SND_SOC_DAPM_OUTPUT("MOUT2"),
+	SND_SOC_DAPM_OUTPUT("MICOUT"),
+	SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0),
+	SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
+
+	SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
+	SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
+	SND_SOC_DAPM_INPUT("MICIN"),
+	SND_SOC_DAPM_INPUT("MICEXT"),
+	SND_SOC_DAPM_INPUT("AUX"),
+	SND_SOC_DAPM_INPUT("MIN"),
+	SND_SOC_DAPM_INPUT("AIN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/*stereo mixer */
+	{"Stereo Mixer", "Playback Switch", "DAC"},
+	{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
+	{"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
+
+	/* mono1 mixer */
+	{"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
+	{"Mono1 Mixer", "Mono Playback Switch", "DAC"},
+
+	/* Mic */
+	{"Mic", NULL, "AIN"},
+	{"Input Mux", "Internal", "Mic Int Bias"},
+	{"Input Mux", "External", "Mic Ext Bias"},
+	{"Mic Int Bias", NULL, "MICIN"},
+	{"Mic Ext Bias", NULL, "MICEXT"},
+	{"MICOUT", NULL, "Input Mux"},
+
+	/* line out */
+	{"LOUT", NULL, "Line Out Enable"},
+	{"ROUT", NULL, "Line Out Enable"},
+	{"Line Out Enable", "Switch", "Line Out"},
+	{"Line Out", NULL, "Stereo Mixer"},
+
+	/* mono1 out */
+	{"MOUT1", NULL, "Mono Out"},
+	{"Mono Out", NULL, "Mono1 Mixer"},
+
+	/* left HP */
+	{"HPL", NULL, "Left HP Enable"},
+	{"Left HP Enable", "Switch", "HP L Amp"},
+	{"HP L Amp", NULL, "Stereo Mixer"},
+
+	/* right HP */
+	{"HPR", NULL, "Right HP Enable"},
+	{"Right HP Enable", "Switch", "HP R Amp"},
+	{"HP R Amp", NULL, "Stereo Mixer"},
+
+	/* speaker */
+	{"SPP", NULL, "Speaker Enable"},
+	{"SPN", NULL, "Speaker Enable"},
+	{"Speaker Enable", "Switch", "Spk Amp"},
+	{"Spk Amp", NULL, "MIN"},
+
+	/* mono 2 */
+	{"MOUT2", NULL, "Mono 2 Enable"},
+	{"Mono 2 Enable", "Switch", "Stereo Mixer"},
+
+	/* Aux In */
+	{"Aux In", NULL, "AUX"},
+
+	/* ADC */
+	{"ADC", NULL, "Input Mixer"},
+	{"Input Mixer", "Mic Capture Switch", "Mic"},
+	{"Input Mixer", "Aux Capture Switch", "Aux In"},
+};
+
+static int ak4535_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, ak4535_dapm_widgets,
+				  ARRAY_SIZE(ak4535_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+	int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ak4535_priv *ak4535 = codec->private_data;
+
+	ak4535->sysclk = freq;
+	return 0;
+}
+
+static int ak4535_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct ak4535_priv *ak4535 = codec->private_data;
+	u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
+	int rate = params_rate(params), fs = 256;
+
+	if (rate)
+		fs = ak4535->sysclk / rate;
+
+	/* set fs */
+	switch (fs) {
+	case 1024:
+		mode2 |= (0x2 << 5);
+		break;
+	case 512:
+		mode2 |= (0x1 << 5);
+		break;
+	case 256:
+		break;
+	}
+
+	/* set rate */
+	ak4535_write(codec, AK4535_MODE2, mode2);
+	return 0;
+}
+
+static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 mode1 = 0;
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		mode1 = 0x0002;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		mode1 = 0x0001;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* use 32 fs for BCLK to save power */
+	mode1 |= 0x4;
+
+	ak4535_write(codec, AK4535_MODE1, mode1);
+	return 0;
+}
+
+static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
+	if (!mute)
+		ak4535_write(codec, AK4535_DAC, mute_reg);
+	else
+		ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
+	return 0;
+}
+
+static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 i;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		ak4535_mute(codec->dai, 0);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		ak4535_mute(codec->dai, 1);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		i = ak4535_read_reg_cache(codec, AK4535_PM1);
+		ak4535_write(codec, AK4535_PM1, i | 0x80);
+		i = ak4535_read_reg_cache(codec, AK4535_PM2);
+		ak4535_write(codec, AK4535_PM2, i & (~0x80));
+		break;
+	case SND_SOC_BIAS_OFF:
+		i = ak4535_read_reg_cache(codec, AK4535_PM1);
+		ak4535_write(codec, AK4535_PM1, i & (~0x80));
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai ak4535_dai = {
+	.name = "AK4535",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4535_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4535_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = ak4535_hw_params,
+	},
+	.dai_ops = {
+		.set_fmt = ak4535_set_dai_fmt,
+		.digital_mute = ak4535_mute,
+		.set_sysclk = ak4535_set_dai_sysclk,
+	},
+};
+EXPORT_SYMBOL_GPL(ak4535_dai);
+
+static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int ak4535_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	ak4535_sync(codec);
+	ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	ak4535_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the AK4535 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int ak4535_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "AK4535";
+	codec->owner = THIS_MODULE;
+	codec->read = ak4535_read_reg_cache;
+	codec->write = ak4535_write;
+	codec->set_bias_level = ak4535_set_bias_level;
+	codec->dai = &ak4535_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(ak4535_reg);
+	codec->reg_cache = kmemdup(ak4535_reg, sizeof(ak4535_reg), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "ak4535: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* power on device */
+	ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	ak4535_add_controls(codec);
+	ak4535_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "ak4535: failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+
+	return ret;
+}
+
+static struct snd_soc_device *ak4535_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+#define I2C_DRIVERID_AK4535 0xfefe /* liam -  need a proper id */
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver ak4535_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+   around */
+static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct snd_soc_device *socdev = ak4535_socdev;
+	struct ak4535_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c;
+	int ret;
+
+	if (addr != setup->i2c_address)
+		return -ENODEV;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
+		goto err;
+	}
+
+	ret = ak4535_init(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "failed to initialise AK4535\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int ak4535_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	i2c_detach_client(client);
+	kfree(codec->reg_cache);
+	kfree(client);
+	return 0;
+}
+
+static int ak4535_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, ak4535_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver ak4535_i2c_driver = {
+	.driver = {
+		.name = "AK4535 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_AK4535,
+	.attach_adapter = ak4535_i2c_attach,
+	.detach_client =  ak4535_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "AK4535",
+	.driver = &ak4535_i2c_driver,
+};
+#endif
+
+static int ak4535_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct ak4535_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct ak4535_priv *ak4535;
+	int ret = 0;
+
+	printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL);
+	if (ak4535 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = ak4535;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ak4535_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		normal_i2c[0] = setup->i2c_address;
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		codec->hw_read = (hw_read_t)i2c_master_recv;
+		ret = i2c_add_driver(&ak4535_i2c_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add i2c driver");
+	}
+#else
+	/* Add other interfaces here */
+#endif
+	return ret;
+}
+
+/* power down chip */
+static int ak4535_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&ak4535_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4535 = {
+	.probe = 	ak4535_probe,
+	.remove = 	ak4535_remove,
+	.suspend = 	ak4535_suspend,
+	.resume =	ak4535_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
+
+MODULE_DESCRIPTION("Soc AK4535 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
new file mode 100644
index 0000000..e9fe30e
--- /dev/null
+++ b/sound/soc/codecs/ak4535.h
@@ -0,0 +1,46 @@
+/*
+ * ak4535.h  --  AK4535 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AK4535_H
+#define _AK4535_H
+
+/* AK4535 register space */
+
+#define AK4535_PM1		0x0
+#define AK4535_PM2		0x1
+#define AK4535_SIG1		0x2
+#define AK4535_SIG2		0x3
+#define AK4535_MODE1		0x4
+#define AK4535_MODE2		0x5
+#define AK4535_DAC		0x6
+#define AK4535_MIC		0x7
+#define AK4535_TIMER		0x8
+#define AK4535_ALC1		0x9
+#define AK4535_ALC2		0xa
+#define AK4535_PGA		0xb
+#define AK4535_LATT		0xc
+#define AK4535_RATT		0xd
+#define AK4535_VOL		0xe
+#define AK4535_STATUS		0xf
+
+#define AK4535_CACHEREGNUM 	0x10
+
+struct ak4535_setup_data {
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ak4535_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4535;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e73fcfd..9deb8c7 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -201,7 +201,7 @@
  * driver what the input settings can be.  This would need to be implemented
  * for stand-alone mode to work.
  */
-static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 				 int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -251,7 +251,7 @@
  * data for playback only, but ASoC currently does not support different
  * formats for playback vs. record.
  */
-static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
 			      unsigned int format)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -471,7 +471,7 @@
  * board does not have the MUTEA or MUTEB pins connected to such circuitry,
  * then this function will do nothing.
  */
-static int cs4270_mute(struct snd_soc_codec_dai *dai, int mute)
+static int cs4270_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	int reg6;
@@ -667,7 +667,7 @@
 
 #endif /* USE_I2C*/
 
-struct snd_soc_codec_dai cs4270_dai = {
+struct snd_soc_dai cs4270_dai = {
 	.name = "CS4270",
 	.playback = {
 		.stream_name = "Playback",
diff --git a/sound/soc/codecs/cs4270.h b/sound/soc/codecs/cs4270.h
index 0ced49b..adc6cd9 100644
--- a/sound/soc/codecs/cs4270.h
+++ b/sound/soc/codecs/cs4270.h
@@ -16,7 +16,7 @@
  * The ASoC codec DAI structure for the CS4270.  Assign this structure to
  * the .codec_dai field of your machine driver's snd_soc_dai_link structure.
  */
-extern struct snd_soc_codec_dai cs4270_dai;
+extern struct snd_soc_dai cs4270_dai;
 
 /*
  * The ASoC codec device structure for the CS4270.  Assign this structure
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 09b1661..b1dce5f 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -29,7 +29,7 @@
  *  ---------------------------------------
  *
  *  Hence the machine layer should disable unsupported inputs/outputs by
- *  snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc.
+ *  snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc.
  */
 
 #include <linux/module.h>
@@ -49,7 +49,7 @@
 #include "tlv320aic3x.h"
 
 #define AUDIO_NAME "aic3x"
-#define AIC3X_VERSION "0.1"
+#define AIC3X_VERSION "0.2"
 
 /* codec private data */
 struct aic3x_priv {
@@ -138,6 +138,20 @@
 		return -EIO;
 }
 
+/*
+ * read from the aic3x register space
+ */
+static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
+		      u8 *value)
+{
+	*value = reg & 0xff;
+	if (codec->hw_read(codec->control_data, value, 1) != 1)
+		return -EIO;
+
+	aic3x_write_reg_cache(codec, reg, *value);
+	return 0;
+}
+
 #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
@@ -192,7 +206,7 @@
 		}
 
 		if (found)
-			snd_soc_dapm_sync_endpoints(widget->codec);
+			snd_soc_dapm_sync(widget->codec);
 	}
 
 	ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
@@ -209,6 +223,8 @@
     { "differential of HPROUT", "constant VCM", "single-ended",
       "differential of HPLCOM", "external feedback" };
 static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
+static const char *aic3x_adc_hpf[] =
+    { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
 
 #define LDAC_ENUM	0
 #define RDAC_ENUM	1
@@ -218,6 +234,7 @@
 #define LINE1R_ENUM	5
 #define LINE2L_ENUM	6
 #define LINE2R_ENUM	7
+#define ADC_HPF_ENUM	8
 
 static const struct soc_enum aic3x_enum[] = {
 	SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
@@ -228,6 +245,7 @@
 	SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
 	SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
 	SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
+	SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
 };
 
 static const struct snd_kcontrol_new aic3x_snd_controls[] = {
@@ -278,6 +296,8 @@
 	/* Input */
 	SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
 	SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
+
+	SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
 };
 
 /* add non dapm controls */
@@ -441,11 +461,34 @@
 	SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
 			 &aic3x_right_line2_mux_controls),
 
+	/*
+	 * Not a real mic bias widget but similar function. This is for dynamic
+	 * control of GPIO1 digital mic modulator clock output function when
+	 * using digital mic.
+	 */
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "GPIO1 dmic modclk",
+			 AIC3X_GPIO1_REG, 4, 0xf,
+			 AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK,
+			 AIC3X_GPIO1_FUNC_DISABLED),
+
+	/*
+	 * Also similar function like mic bias. Selects digital mic with
+	 * configurable oversampling rate instead of ADC converter.
+	 */
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 128",
+			 AIC3X_ASD_INTF_CTRLA, 0, 3, 1, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 64",
+			 AIC3X_ASD_INTF_CTRLA, 0, 3, 2, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32",
+			 AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
+
 	/* Mic Bias */
-	SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0),
-	SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0),
-	SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0),
-	SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V",
+			 MICBIAS_CTRL, 6, 3, 1, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V",
+			 MICBIAS_CTRL, 6, 3, 2, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD",
+			 MICBIAS_CTRL, 6, 3, 3, 0),
 
 	/* Left PGA to Left Output bypass */
 	SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0,
@@ -483,7 +526,7 @@
 	SND_SOC_DAPM_INPUT("LINE2R"),
 };
 
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
 	/* Left Output */
 	{"Left DAC Mux", "DAC_L1", "Left DAC"},
 	{"Left DAC Mux", "DAC_L2", "Left DAC"},
@@ -554,6 +597,7 @@
 	{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
 
 	{"Left ADC", NULL, "Left PGA Mixer"},
+	{"Left ADC", NULL, "GPIO1 dmic modclk"},
 
 	/* Right Input */
 	{"Right Line1R Mux", "single-ended", "LINE1R"},
@@ -567,6 +611,7 @@
 	{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
 
 	{"Right ADC", NULL, "Right PGA Mixer"},
+	{"Right ADC", NULL, "GPIO1 dmic modclk"},
 
 	/* Left PGA Bypass */
 	{"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
@@ -628,101 +673,27 @@
 	{"Mono Out", NULL, "Right Line2 Bypass Mixer"},
 	{"Right HP Out", NULL, "Right Line2 Bypass Mixer"},
 
-	/* terminator */
-	{NULL, NULL, NULL},
+	/*
+	 * Logical path between digital mic enable and GPIO1 modulator clock
+	 * output function
+	 */
+	{"GPIO1 dmic modclk", NULL, "DMic Rate 128"},
+	{"GPIO1 dmic modclk", NULL, "DMic Rate 64"},
+	{"GPIO1 dmic modclk", NULL, "DMic Rate 32"},
 };
 
 static int aic3x_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+				  ARRAY_SIZE(aic3x_dapm_widgets));
 
 	/* set up audio path interconnects */
-	for (i = 0; intercon[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, intercon[i][0],
-					   intercon[i][1], intercon[i][2]);
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
 }
 
-struct aic3x_rate_divs {
-	u32 mclk;
-	u32 rate;
-	u32 fsref_reg;
-	u8 sr_reg:4;
-	u8 pllj_reg;
-	u16 plld_reg;
-};
-
-/* AIC3X codec mclk clock divider coefficients */
-static const struct aic3x_rate_divs aic3x_divs[] = {
-	/* 8k */
-	{12000000, 8000, 48000, 0xa, 16, 3840},
-	{19200000, 8000, 48000, 0xa, 10, 2400},
-	{22579200, 8000, 48000, 0xa, 8, 7075},
-	{33868800, 8000, 48000, 0xa, 5, 8049},
-	/* 11.025k */
-	{12000000, 11025, 44100, 0x6, 15, 528},
-	{19200000, 11025, 44100, 0x6, 9, 4080},
-	{22579200, 11025, 44100, 0x6, 8, 0},
-	{33868800, 11025, 44100, 0x6, 5, 3333},
-	/* 16k */
-	{12000000, 16000, 48000, 0x4, 16, 3840},
-	{19200000, 16000, 48000, 0x4, 10, 2400},
-	{22579200, 16000, 48000, 0x4, 8, 7075},
-	{33868800, 16000, 48000, 0x4, 5, 8049},
-	/* 22.05k */
-	{12000000, 22050, 44100, 0x2, 15, 528},
-	{19200000, 22050, 44100, 0x2, 9, 4080},
-	{22579200, 22050, 44100, 0x2, 8, 0},
-	{33868800, 22050, 44100, 0x2, 5, 3333},
-	/* 32k */
-	{12000000, 32000, 48000, 0x1, 16, 3840},
-	{19200000, 32000, 48000, 0x1, 10, 2400},
-	{22579200, 32000, 48000, 0x1, 8, 7075},
-	{33868800, 32000, 48000, 0x1, 5, 8049},
-	/* 44.1k */
-	{12000000, 44100, 44100, 0x0, 15, 528},
-	{19200000, 44100, 44100, 0x0, 9, 4080},
-	{22579200, 44100, 44100, 0x0, 8, 0},
-	{33868800, 44100, 44100, 0x0, 5, 3333},
-	/* 48k */
-	{12000000, 48000, 48000, 0x0, 16, 3840},
-	{19200000, 48000, 48000, 0x0, 10, 2400},
-	{22579200, 48000, 48000, 0x0, 8, 7075},
-	{33868800, 48000, 48000, 0x0, 5, 8049},
-	/* 64k */
-	{12000000, 64000, 96000, 0x1, 16, 3840},
-	{19200000, 64000, 96000, 0x1, 10, 2400},
-	{22579200, 64000, 96000, 0x1, 8, 7075},
-	{33868800, 64000, 96000, 0x1, 5, 8049},
-	/* 88.2k */
-	{12000000, 88200, 88200, 0x0, 15, 528},
-	{19200000, 88200, 88200, 0x0, 9, 4080},
-	{22579200, 88200, 88200, 0x0, 8, 0},
-	{33868800, 88200, 88200, 0x0, 5, 3333},
-	/* 96k */
-	{12000000, 96000, 96000, 0x0, 16, 3840},
-	{19200000, 96000, 96000, 0x0, 10, 2400},
-	{22579200, 96000, 96000, 0x0, 8, 7075},
-	{33868800, 96000, 96000, 0x0, 5, 8049},
-};
-
-static inline int aic3x_get_divs(int mclk, int rate)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) {
-		if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk)
-			return i;
-	}
-
-	return 0;
-}
-
 static int aic3x_hw_params(struct snd_pcm_substream *substream,
 			   struct snd_pcm_hw_params *params)
 {
@@ -730,57 +701,9 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
-	int i;
-	u8 data, pll_p, pll_r, pll_j;
-	u16 pll_d;
-
-	i = aic3x_get_divs(aic3x->sysclk, params_rate(params));
-
-	/* Route Left DAC to left channel input and
-	 * right DAC to right channel input */
-	data = (LDAC2LCH | RDAC2RCH);
-	switch (aic3x_divs[i].fsref_reg) {
-	case 44100:
-		data |= FSREF_44100;
-		break;
-	case 48000:
-		data |= FSREF_48000;
-		break;
-	case 88200:
-		data |= FSREF_44100 | DUAL_RATE_MODE;
-		break;
-	case 96000:
-		data |= FSREF_48000 | DUAL_RATE_MODE;
-		break;
-	}
-	aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
-
-	/* codec sample rate select */
-	data = aic3x_divs[i].sr_reg;
-	data |= (data << 4);
-	aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
-
-	/* Use PLL for generation Fsref by equation:
-	 * Fsref = (MCLK * K * R)/(2048 * P);
-	 * Fix P = 2 and R = 1 and calculate K, if
-	 * K = J.D, i.e. J - an interger portion of K and D is the fractional
-	 * one with 4 digits of precision;
-	 * Example:
-	 * For MCLK = 22.5792 MHz and Fsref = 48kHz:
-	 * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074
-	 */
-	pll_p = 2;
-	pll_r = 1;
-	pll_j = aic3x_divs[i].pllj_reg;
-	pll_d = aic3x_divs[i].plld_reg;
-
-	data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-	aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
-	aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
-	aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
-	aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT);
-	aic3x_write(codec, AIC3X_PLL_PROGD_REG,
-		    (pll_d & 0x3F) << PLLD_LSB_SHIFT);
+	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
+	u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+	u16 pll_d = 1;
 
 	/* select data word length */
 	data =
@@ -800,10 +723,98 @@
 	}
 	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
 
+	/* Fsref can be 44100 or 48000 */
+	fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+
+	/* Try to find a value for Q which allows us to bypass the PLL and
+	 * generate CODEC_CLK directly. */
+	for (pll_q = 2; pll_q < 18; pll_q++)
+		if (aic3x->sysclk / (128 * pll_q) == fsref) {
+			bypass_pll = 1;
+			break;
+		}
+
+	if (bypass_pll) {
+		pll_q &= 0xf;
+		aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
+		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
+	} else
+		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+
+	/* Route Left DAC to left channel input and
+	 * right DAC to right channel input */
+	data = (LDAC2LCH | RDAC2RCH);
+	data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
+	if (params_rate(params) >= 64000)
+		data |= DUAL_RATE_MODE;
+	aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
+
+	/* codec sample rate select */
+	data = (fsref * 20) / params_rate(params);
+	if (params_rate(params) < 64000)
+		data /= 2;
+	data /= 5;
+	data -= 2;
+	data |= (data << 4);
+	aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
+
+	if (bypass_pll)
+		return 0;
+
+	/* Use PLL
+	 * find an apropriate setup for j, d, r and p by iterating over
+	 * p and r - j and d are calculated for each fraction.
+	 * Up to 128 values are probed, the closest one wins the game.
+	 * The sysclk is divided by 1000 to prevent integer overflows.
+	 */
+	codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
+
+	for (r = 1; r <= 16; r++)
+		for (p = 1; p <= 8; p++) {
+			int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
+			u8 j = tmp / 10000;
+			u16 d = tmp % 10000;
+
+			if (j > 63)
+				continue;
+
+			if (d != 0 && aic3x->sysclk < 10000000)
+				continue;
+
+			/* This is actually 1000 * ((j + (d/10000)) * r) / p
+			 * The term had to be converted to get rid of the
+			 * division by 10000 */
+			clk = ((10000 * j * r) + (d * r)) / (10 * p);
+
+			/* check whether this values get closer than the best
+			 * ones we had before */
+			if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+				pll_j = j; pll_d = d; pll_r = r; pll_p = p;
+				last_clk = clk;
+			}
+
+			/* Early exit for exact matches */
+			if (clk == codec_clk)
+				break;
+		}
+
+	if (last_clk == 0) {
+		printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
+		return -EINVAL;
+	}
+
+	data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+	aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
+	aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
+	aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT);
+	aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT);
+	aic3x_write(codec, AIC3X_PLL_PROGD_REG,
+		    (pll_d & 0x3F) << PLLD_LSB_SHIFT);
+
 	return 0;
 }
 
-static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute)
+static int aic3x_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON;
@@ -820,31 +831,25 @@
 	return 0;
 }
 
-static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 				int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
 
-	switch (freq) {
-	case 12000000:
-	case 19200000:
-	case 22579200:
-	case 33868800:
-		aic3x->sysclk = freq;
-		return 0;
-	}
-
-	return -EINVAL;
+	aic3x->sysclk = freq;
+	return 0;
 }
 
-static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 			     unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
-	u8 iface_areg = 0;
-	u8 iface_breg = 0;
+	u8 iface_areg, iface_breg;
+
+	iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
+	iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -883,13 +888,14 @@
 	return 0;
 }
 
-static int aic3x_dapm_event(struct snd_soc_codec *codec, int event)
+static int aic3x_set_bias_level(struct snd_soc_codec *codec,
+				enum snd_soc_bias_level level)
 {
 	struct aic3x_priv *aic3x = codec->private_data;
 	u8 reg;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0:
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* all power is driven by DAPM system */
 		if (aic3x->master) {
 			/* enable pll */
@@ -898,10 +904,9 @@
 				    reg | PLL_ENABLE);
 		}
 		break;
-	case SNDRV_CTL_POWER_D1:
-	case SNDRV_CTL_POWER_D2:
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot:
+	case SND_SOC_BIAS_STANDBY:
 		/*
 		 * all power is driven by DAPM system,
 		 * so output power is safe if bypass was set
@@ -913,7 +918,7 @@
 				    reg & ~PLL_ENABLE);
 		}
 		break;
-	case SNDRV_CTL_POWER_D3cold:
+	case SND_SOC_BIAS_OFF:
 		/* force all power off */
 		reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
 		aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
@@ -949,16 +954,43 @@
 		}
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 
 	return 0;
 }
 
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state)
+{
+	u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+	u8 bit = gpio ? 3: 0;
+	u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit);
+	aic3x_write(codec, reg, val | (!!state << bit));
+}
+EXPORT_SYMBOL_GPL(aic3x_set_gpio);
+
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
+{
+	u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG;
+	u8 val, bit = gpio ? 2: 1;
+
+	aic3x_read(codec, reg, &val);
+	return (val >> bit) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_get_gpio);
+
+int aic3x_headset_detected(struct snd_soc_codec *codec)
+{
+	u8 val;
+	aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
+	return (val >> 2) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_headset_detected);
+
 #define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000
 #define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
 
-struct snd_soc_codec_dai aic3x_dai = {
+struct snd_soc_dai aic3x_dai = {
 	.name = "aic3x",
 	.playback = {
 		.stream_name = "Playback",
@@ -988,7 +1020,7 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->codec;
 
-	aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	return 0;
 }
@@ -1008,7 +1040,7 @@
 		codec->hw_write(codec->control_data, data, 2);
 	}
 
-	aic3x_dapm_event(codec, codec->suspend_dapm_state);
+	aic3x_set_bias_level(codec, codec->suspend_bias_level);
 
 	return 0;
 }
@@ -1020,16 +1052,17 @@
 static int aic3x_init(struct snd_soc_device *socdev)
 {
 	struct snd_soc_codec *codec = socdev->codec;
+	struct aic3x_setup_data *setup = socdev->codec_data;
 	int reg, ret = 0;
 
 	codec->name = "aic3x";
 	codec->owner = THIS_MODULE;
 	codec->read = aic3x_read_reg_cache;
 	codec->write = aic3x_write;
-	codec->dapm_event = aic3x_dapm_event;
+	codec->set_bias_level = aic3x_set_bias_level;
 	codec->dai = &aic3x_dai;
 	codec->num_dai = 1;
-	codec->reg_cache_size = sizeof(aic3x_reg);
+	codec->reg_cache_size = ARRAY_SIZE(aic3x_reg);
 	codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
@@ -1108,7 +1141,11 @@
 	aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL);
 
 	/* off, with power on */
-	aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* setup GPIO functions */
+	aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
+	aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
 
 	aic3x_add_controls(codec);
 	aic3x_add_widgets(codec);
@@ -1217,6 +1254,12 @@
 	.name = "AIC3X",
 	.driver = &aic3x_i2c_driver,
 };
+
+static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
+{
+	value[0] = i2c_smbus_read_byte_data(client, value[0]);
+	return (len == 1);
+}
 #endif
 
 static int aic3x_probe(struct platform_device *pdev)
@@ -1251,6 +1294,7 @@
 	if (setup->i2c_address) {
 		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t) i2c_master_send;
+		codec->hw_read = (hw_read_t) aic3x_i2c_read;
 		ret = i2c_add_driver(&aic3x_i2c_driver);
 		if (ret != 0)
 			printk(KERN_ERR "can't add i2c driver");
@@ -1268,7 +1312,7 @@
 
 	/* power down chip */
 	if (codec->control_data)
-		aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3);
+		aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index d0cdeeb..d76c079 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -37,6 +37,8 @@
 #define AIC3X_ASD_INTF_CTRLB		9
 /* Audio overflow status and PLL R value programming register */
 #define AIC3X_OVRF_STATUS_AND_PLLR_REG	11
+/* Audio codec digital filter control register */
+#define AIC3X_CODEC_DFILT_CTRL		12
 
 /* ADC PGA Gain control registers */
 #define LADC_VOL			15
@@ -108,6 +110,13 @@
 #define DACR1_2_RLOPM_VOL		92
 #define LLOPM_CTRL			86
 #define RLOPM_CTRL			93
+/* GPIO/IRQ registers */
+#define AIC3X_STICKY_IRQ_FLAGS_REG	96
+#define AIC3X_RT_IRQ_FLAGS_REG		97
+#define AIC3X_GPIO1_REG			98
+#define AIC3X_GPIO2_REG			99
+#define AIC3X_GPIOA_REG			100
+#define AIC3X_GPIOB_REG			101
 /* Clock generation control register */
 #define AIC3X_CLKGEN_CTRL_REG		102
 
@@ -128,12 +137,15 @@
 
 /* PLL registers bitfields */
 #define PLLP_SHIFT		0
+#define PLLQ_SHIFT		3
 #define PLLR_SHIFT		0
 #define PLLJ_SHIFT		2
 #define PLLD_MSB_SHIFT		0
 #define PLLD_LSB_SHIFT		2
 
 /* Clock generation register bits */
+#define CODEC_CLKIN_PLLDIV	0
+#define CODEC_CLKIN_CLKDIV	1
 #define PLL_CLKIN_SHIFT		4
 #define MCLK_SOURCE		0x0
 #define PLL_CLKDIV_SHIFT	0
@@ -171,11 +183,52 @@
 /* Default input volume */
 #define DEFAULT_GAIN    0x20
 
-struct aic3x_setup_data {
-	unsigned short i2c_address;
+/* GPIO API */
+enum {
+	AIC3X_GPIO1_FUNC_DISABLED		= 0,
+	AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC	= 1,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX		= 2,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2		= 3,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4		= 4,
+	AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8		= 5,
+	AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ	= 6,
+	AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ		= 7,
+	AIC3X_GPIO1_FUNC_INPUT			= 8,
+	AIC3X_GPIO1_FUNC_OUTPUT			= 9,
+	AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK	= 10,
+	AIC3X_GPIO1_FUNC_AUDIO_WORDCLK		= 11,
+	AIC3X_GPIO1_FUNC_BUTTON_IRQ		= 12,
+	AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ	= 13,
+	AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ	= 14,
+	AIC3X_GPIO1_FUNC_ALL_IRQ		= 16
 };
 
-extern struct snd_soc_codec_dai aic3x_dai;
+enum {
+	AIC3X_GPIO2_FUNC_DISABLED		= 0,
+	AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ	= 2,
+	AIC3X_GPIO2_FUNC_INPUT			= 3,
+	AIC3X_GPIO2_FUNC_OUTPUT			= 4,
+	AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT	= 5,
+	AIC3X_GPIO2_FUNC_AUDIO_BITCLK		= 8,
+	AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9,
+	AIC3X_GPIO2_FUNC_ALL_IRQ		= 10,
+	AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11,
+	AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12,
+	AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ	= 13,
+	AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ		= 14,
+	AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ	= 15
+};
+
+void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
+int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
+int aic3x_headset_detected(struct snd_soc_codec *codec);
+
+struct aic3x_setup_data {
+	unsigned short i2c_address;
+	unsigned int gpio_func[2];
+};
+
+extern struct snd_soc_dai aic3x_dai;
 extern struct snd_soc_codec_device soc_codec_dev_aic3x;
 
 #endif /* _AIC3X_H */
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
new file mode 100644
index 0000000..a52d6d9
--- /dev/null
+++ b/sound/soc/codecs/uda1380.c
@@ -0,0 +1,852 @@
+/*
+ * uda1380.c - Philips UDA1380 ALSA SoC audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
+ * Improved support for DAPM and audio routing/mixing capabilities,
+ * added TLV support.
+ *
+ * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
+ * codec model.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright 2005 Openedhand Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "uda1380.h"
+
+#define UDA1380_VERSION "0.6"
+#define AUDIO_NAME "uda1380"
+
+/*
+ * uda1380 register cache
+ */
+static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
+	0x0502, 0x0000, 0x0000, 0x3f3f,
+	0x0202, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0xff00, 0x0000, 0x4800,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x8000, 0x0002, 0x0000,
+};
+
+/*
+ * read uda1380 register cache
+ */
+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg == UDA1380_RESET)
+		return 0;
+	if (reg >= UDA1380_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write uda1380 register cache
+ */
+static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= UDA1380_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the UDA1380 register space
+ */
+static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[3];
+
+	/* data is
+	 *   data[0] is register offset
+	 *   data[1] is MS byte
+	 *   data[2] is LS byte
+	 */
+	data[0] = reg;
+	data[1] = (value & 0xff00) >> 8;
+	data[2] = value & 0x00ff;
+
+	uda1380_write_reg_cache(codec, reg, value);
+
+	/* the interpolator & decimator regs must only be written when the
+	 * codec DAI is active.
+	 */
+	if (!codec->active && (reg >= UDA1380_MVOL))
+		return 0;
+	pr_debug("uda1380: hw write %x val %x\n", reg, value);
+	if (codec->hw_write(codec->control_data, data, 3) == 3) {
+		unsigned int val;
+		i2c_master_send(codec->control_data, data, 1);
+		i2c_master_recv(codec->control_data, data, 2);
+		val = (data[0]<<8) | data[1];
+		if (val != value) {
+			pr_debug("uda1380: READ BACK VAL %x\n",
+					(data[0]<<8) | data[1]);
+			return -EIO;
+		}
+		return 0;
+	} else
+		return -EIO;
+}
+
+#define uda1380_reset(c)	uda1380_write(c, UDA1380_RESET, 0)
+
+/* declarations of ALSA reg_elem_REAL controls */
+static const char *uda1380_deemp[] = {
+	"None",
+	"32kHz",
+	"44.1kHz",
+	"48kHz",
+	"96kHz",
+};
+static const char *uda1380_input_sel[] = {
+	"Line",
+	"Mic + Line R",
+	"Line L",
+	"Mic",
+};
+static const char *uda1380_output_sel[] = {
+	"DAC",
+	"Analog Mixer",
+};
+static const char *uda1380_spf_mode[] = {
+	"Flat",
+	"Minimum1",
+	"Minimum2",
+	"Maximum"
+};
+static const char *uda1380_capture_sel[] = {
+	"ADC",
+	"Digital Mixer"
+};
+static const char *uda1380_sel_ns[] = {
+	"3rd-order",
+	"5th-order"
+};
+static const char *uda1380_mix_control[] = {
+	"off",
+	"PCM only",
+	"before sound processing",
+	"after sound processing"
+};
+static const char *uda1380_sdet_setting[] = {
+	"3200",
+	"4800",
+	"9600",
+	"19200"
+};
+static const char *uda1380_os_setting[] = {
+	"single-speed",
+	"double-speed (no mixing)",
+	"quad-speed (no mixing)"
+};
+
+static const struct soc_enum uda1380_deemp_enum[] = {
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp),
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp),
+};
+static const struct soc_enum uda1380_input_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel);		/* SEL_MIC, SEL_LNA */
+static const struct soc_enum uda1380_output_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel);		/* R02_EN_AVC */
+static const struct soc_enum uda1380_spf_enum =
+	SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode);		/* M */
+static const struct soc_enum uda1380_capture_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel);	/* SEL_SOURCE */
+static const struct soc_enum uda1380_sel_ns_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns);		/* SEL_NS */
+static const struct soc_enum uda1380_mix_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control);	/* MIX, MIX_POS */
+static const struct soc_enum uda1380_sdet_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting);	/* SD_VALUE */
+static const struct soc_enum uda1380_os_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting);	/* OS */
+
+/*
+ * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB)
+ */
+static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);
+
+/*
+ * from -78 dB in 1 dB steps (3 dB steps, really. LSB are ignored),
+ * from -66 dB in 0.5 dB steps (2 dB steps, really) and
+ * from -52 dB in 0.25 dB steps
+ */
+static const unsigned int mvol_tlv[] = {
+	TLV_DB_RANGE_HEAD(3),
+	0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1),
+	16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0),
+	44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0),
+};
+
+/*
+ * from -72 dB in 1.5 dB steps (6 dB steps really),
+ * from -66 dB in 0.75 dB steps (3 dB steps really),
+ * from -60 dB in 0.5 dB steps (2 dB steps really) and
+ * from -46 dB in 0.25 dB steps
+ */
+static const unsigned int vc_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1),
+	8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0),
+	16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0),
+	44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0),
+};
+
+/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */
+static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);
+
+/* from 0 to 24 dB in 2 dB steps, if SPF mode == maximum, otherwise cuts
+ * off at 18 dB max) */
+static DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0);
+
+/* from -63 to 24 dB in 0.5 dB steps (-128...48) */
+static DECLARE_TLV_DB_SCALE(dec_tlv, -6400, 50, 1);
+
+/* from 0 to 24 dB in 3 dB steps */
+static DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+
+/* from 0 to 30 dB in 2 dB steps */
+static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);
+
+static const struct snd_kcontrol_new uda1380_snd_controls[] = {
+	SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv),	/* AVCR, AVCL */
+	SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv),	/* MVCL, MVCR */
+	SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv),	/* VC2 */
+	SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv),	/* VC1 */
+	SOC_ENUM("Sound Processing Filter", uda1380_spf_enum),				/* M */
+	SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), 	/* TRL, TRR */
+	SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv),	/* BBL, BBR */
+/**/	SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1),		/* MTM */
+	SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1),		/* MT2 from decimation filter */
+	SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]),		/* DE2 */
+	SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1),		/* MT1, from digital data input */
+	SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]),		/* DE1 */
+	SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0),	/* DA_POL_INV */
+	SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum),				/* SEL_NS */
+	SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum),		/* MIX_POS, MIX */
+	SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0),			/* SILENCE, force DAC output to silence */
+	SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0),		/* SDET_ON */
+	SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum),		/* SD_VALUE */
+	SOC_ENUM("Oversampling Input", uda1380_os_enum),			/* OS */
+	SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv),	/* ML_DEC, MR_DEC */
+/**/	SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1),		/* MT_ADC */
+	SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */
+	SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0),	/* ADCPOL_INV */
+	SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv),	/* VGA_CTRL */
+	SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0),		/* SKIP_DCFIL (before decimator) */
+	SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0),		/* EN_DCFIL (at output of decimator) */
+	SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0),			/* TODO: enum, see table 62 */
+	SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1),			/* AGC_LEVEL */
+	/* -5.5, -8, -11.5, -14 dBFS */
+	SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
+};
+
+/* add non dapm controls */
+static int uda1380_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+			snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Input mux */
+static const struct snd_kcontrol_new uda1380_input_mux_control =
+	SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
+
+/* Output mux */
+static const struct snd_kcontrol_new uda1380_output_mux_control =
+	SOC_DAPM_ENUM("Route", uda1380_output_sel_enum);
+
+/* Capture mux */
+static const struct snd_kcontrol_new uda1380_capture_mux_control =
+	SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum);
+
+
+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
+	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+		&uda1380_input_mux_control),
+	SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
+		&uda1380_output_mux_control),
+	SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+		&uda1380_capture_mux_control),
+	SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
+	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
+	SND_SOC_DAPM_INPUT("VINM"),
+	SND_SOC_DAPM_INPUT("VINL"),
+	SND_SOC_DAPM_INPUT("VINR"),
+	SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("VOUTLHP"),
+	SND_SOC_DAPM_OUTPUT("VOUTRHP"),
+	SND_SOC_DAPM_OUTPUT("VOUTL"),
+	SND_SOC_DAPM_OUTPUT("VOUTR"),
+	SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
+	SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+	/* output mux */
+	{"HeadPhone Driver", NULL, "Output Mux"},
+	{"VOUTR", NULL, "Output Mux"},
+	{"VOUTL", NULL, "Output Mux"},
+
+	{"Analog Mixer", NULL, "VINR"},
+	{"Analog Mixer", NULL, "VINL"},
+	{"Analog Mixer", NULL, "DAC"},
+
+	{"Output Mux", "DAC", "DAC"},
+	{"Output Mux", "Analog Mixer", "Analog Mixer"},
+
+	/* {"DAC", "Digital Mixer", "I2S" } */
+
+	/* headphone driver */
+	{"VOUTLHP", NULL, "HeadPhone Driver"},
+	{"VOUTRHP", NULL, "HeadPhone Driver"},
+
+	/* input mux */
+	{"Left ADC", NULL, "Input Mux"},
+	{"Input Mux", "Mic", "Mic LNA"},
+	{"Input Mux", "Mic + Line R", "Mic LNA"},
+	{"Input Mux", "Line L", "Left PGA"},
+	{"Input Mux", "Line", "Left PGA"},
+
+	/* right input */
+	{"Right ADC", "Mic + Line R", "Right PGA"},
+	{"Right ADC", "Line", "Right PGA"},
+
+	/* inputs */
+	{"Mic LNA", NULL, "VINM"},
+	{"Left PGA", NULL, "VINL"},
+	{"Right PGA", NULL, "VINR"},
+};
+
+static int uda1380_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
+				  ARRAY_SIZE(uda1380_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int iface;
+
+	/* set up DAI based upon fmt */
+	iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
+	iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
+
+	/* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= R01_SFORI_I2S | R01_SFORO_I2S;
+		break;
+	case SND_SOC_DAIFMT_LSB:
+		iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
+		break;
+	case SND_SOC_DAIFMT_MSB:
+		iface |= R01_SFORI_MSB | R01_SFORO_I2S;
+	}
+
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
+		iface |= R01_SIM;
+
+	uda1380_write(codec, UDA1380_IFACE, iface);
+
+	return 0;
+}
+
+/*
+ * Flush reg cache
+ * We can only write the interpolator and decimator registers
+ * when the DAI is being clocked by the CPU DAI. It's up to the
+ * machine and cpu DAI driver to do this before we are called.
+ */
+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int reg, reg_start, reg_end, clk;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		reg_start = UDA1380_MVOL;
+		reg_end = UDA1380_MIXER;
+	} else {
+		reg_start = UDA1380_DEC;
+		reg_end = UDA1380_AGC;
+	}
+
+	/* FIXME disable DAC_CLK */
+	clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+	uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
+
+	for (reg = reg_start; reg <= reg_end; reg++) {
+		pr_debug("uda1380: flush reg %x val %x:", reg,
+				uda1380_read_reg_cache(codec, reg));
+		uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
+	}
+
+	/* FIXME enable DAC_CLK */
+	uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
+
+	return 0;
+}
+
+static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+	/* set WSPLL power and divider if running from this clock */
+	if (clk & R00_DAC_CLK) {
+		int rate = params_rate(params);
+		u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+		clk &= ~0x3; /* clear SEL_LOOP_DIV */
+		switch (rate) {
+		case 6250 ... 12500:
+			clk |= 0x0;
+			break;
+		case 12501 ... 25000:
+			clk |= 0x1;
+			break;
+		case 25001 ... 50000:
+			clk |= 0x2;
+			break;
+		case 50001 ... 100000:
+			clk |= 0x3;
+			break;
+		}
+		uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		clk |= R00_EN_DAC | R00_EN_INT;
+	else
+		clk |= R00_EN_ADC | R00_EN_DEC;
+
+	uda1380_write(codec, UDA1380_CLK, clk);
+	return 0;
+}
+
+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+
+	/* shut down WSPLL power if running from this clock */
+	if (clk & R00_DAC_CLK) {
+		u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+		uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		clk &= ~(R00_EN_DAC | R00_EN_INT);
+	else
+		clk &= ~(R00_EN_ADC | R00_EN_DEC);
+
+	uda1380_write(codec, UDA1380_CLK, clk);
+}
+
+static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
+
+	/* FIXME: mute(codec,0) is called when the magician clock is already
+	 * set to WSPLL, but for some unknown reason writing to interpolator
+	 * registers works only when clocked by SYSCLK */
+	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
+	uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
+	if (mute)
+		uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
+	else
+		uda1380_write(codec, UDA1380_DEEMP, mute_reg);
+	uda1380_write(codec, UDA1380_CLK, clk);
+	return 0;
+}
+
+static int uda1380_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
+		break;
+	case SND_SOC_BIAS_OFF:
+		uda1380_write(codec, UDA1380_PM, 0x0);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		       SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+		       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai uda1380_dai[] = {
+{
+	.name = "UDA1380",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = uda1380_pcm_hw_params,
+		.shutdown = uda1380_pcm_shutdown,
+		.prepare = uda1380_pcm_prepare,
+	},
+	.dai_ops = {
+		.digital_mute = uda1380_mute,
+		.set_fmt = uda1380_set_dai_fmt,
+	},
+},
+{ /* playback only - dual interface */
+	.name = "UDA1380",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = {
+		.hw_params = uda1380_pcm_hw_params,
+		.shutdown = uda1380_pcm_shutdown,
+		.prepare = uda1380_pcm_prepare,
+	},
+	.dai_ops = {
+		.digital_mute = uda1380_mute,
+		.set_fmt = uda1380_set_dai_fmt,
+	},
+},
+{ /* capture only - dual interface*/
+	.name = "UDA1380",
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA1380_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = {
+		.hw_params = uda1380_pcm_hw_params,
+		.shutdown = uda1380_pcm_shutdown,
+		.prepare = uda1380_pcm_prepare,
+	},
+	.dai_ops = {
+		.set_fmt = uda1380_set_dai_fmt,
+	},
+},
+};
+EXPORT_SYMBOL_GPL(uda1380_dai);
+
+static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int uda1380_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	uda1380_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the UDA1380 driver
+ * register mixer and dsp interfaces with the kernel
+ */
+static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "UDA1380";
+	codec->owner = THIS_MODULE;
+	codec->read = uda1380_read_reg_cache;
+	codec->write = uda1380_write;
+	codec->set_bias_level = uda1380_set_bias_level;
+	codec->dai = uda1380_dai;
+	codec->num_dai = ARRAY_SIZE(uda1380_dai);
+	codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
+				   GFP_KERNEL);
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+	codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
+	codec->reg_cache_step = 1;
+	uda1380_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		pr_err("uda1380: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* power on device */
+	uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	/* set clock input */
+	switch (dac_clk) {
+	case UDA1380_DAC_CLK_SYSCLK:
+		uda1380_write(codec, UDA1380_CLK, 0);
+		break;
+	case UDA1380_DAC_CLK_WSPLL:
+		uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
+		break;
+	}
+
+	/* uda1380 init */
+	uda1380_add_controls(codec);
+	uda1380_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		pr_err("uda1380: failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static struct snd_soc_device *uda1380_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+#define I2C_DRIVERID_UDA1380 0xfefe /* liam -  need a proper id */
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver uda1380_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+   around */
+
+static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct snd_soc_device *socdev = uda1380_socdev;
+	struct uda1380_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c;
+	int ret;
+
+	if (addr != setup->i2c_address)
+		return -ENODEV;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		pr_err("uda1380: failed to attach codec at addr %x\n", addr);
+		goto err;
+	}
+
+	ret = uda1380_init(socdev, setup->dac_clk);
+	if (ret < 0) {
+		pr_err("uda1380: failed to initialise UDA1380\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int uda1380_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	i2c_detach_client(client);
+	kfree(codec->reg_cache);
+	kfree(client);
+	return 0;
+}
+
+static int uda1380_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, uda1380_codec_probe);
+}
+
+static struct i2c_driver uda1380_i2c_driver = {
+	.driver = {
+		.name =  "UDA1380 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_UDA1380,
+	.attach_adapter = uda1380_i2c_attach,
+	.detach_client =  uda1380_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "UDA1380",
+	.driver = &uda1380_i2c_driver,
+};
+#endif
+
+static int uda1380_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct uda1380_setup_data *setup;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	uda1380_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		normal_i2c[0] = setup->i2c_address;
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = i2c_add_driver(&uda1380_i2c_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add i2c driver");
+	}
+#else
+	/* Add other interfaces here */
+#endif
+	return ret;
+}
+
+/* power down chip */
+static int uda1380_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&uda1380_i2c_driver);
+#endif
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
+	.probe = 	uda1380_probe,
+	.remove = 	uda1380_remove,
+	.suspend = 	uda1380_suspend,
+	.resume =	uda1380_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+
+MODULE_AUTHOR("Giorgio Padrin");
+MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
new file mode 100644
index 0000000..50c603e
--- /dev/null
+++ b/sound/soc/codecs/uda1380.h
@@ -0,0 +1,89 @@
+/*
+ * Audio support for Philips UDA1380
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ */
+
+#ifndef _UDA1380_H
+#define _UDA1380_H
+
+#define UDA1380_CLK	0x00
+#define UDA1380_IFACE	0x01
+#define UDA1380_PM	0x02
+#define UDA1380_AMIX	0x03
+#define UDA1380_HP	0x04
+#define UDA1380_MVOL	0x10
+#define UDA1380_MIXVOL	0x11
+#define UDA1380_MODE	0x12
+#define UDA1380_DEEMP	0x13
+#define UDA1380_MIXER	0x14
+#define UDA1380_INTSTAT	0x18
+#define UDA1380_DEC	0x20
+#define UDA1380_PGA	0x21
+#define UDA1380_ADC	0x22
+#define UDA1380_AGC	0x23
+#define UDA1380_DECSTAT	0x28
+#define UDA1380_RESET	0x7f
+
+#define UDA1380_CACHEREGNUM 0x24
+
+/* Register flags */
+#define R00_EN_ADC	0x0800
+#define R00_EN_DEC	0x0400
+#define R00_EN_DAC	0x0200
+#define R00_EN_INT	0x0100
+#define R00_DAC_CLK	0x0010
+#define R01_SFORI_I2S   0x0000
+#define R01_SFORI_LSB16 0x0100
+#define R01_SFORI_LSB18 0x0200
+#define R01_SFORI_LSB20 0x0300
+#define R01_SFORI_MSB   0x0500
+#define R01_SFORI_MASK  0x0700
+#define R01_SFORO_I2S   0x0000
+#define R01_SFORO_LSB16 0x0001
+#define R01_SFORO_LSB18 0x0002
+#define R01_SFORO_LSB20 0x0003
+#define R01_SFORO_LSB24 0x0004
+#define R01_SFORO_MSB   0x0005
+#define R01_SFORO_MASK  0x0007
+#define R01_SEL_SOURCE  0x0040
+#define R01_SIM		0x0010
+#define R02_PON_PLL	0x8000
+#define R02_PON_HP	0x2000
+#define R02_PON_DAC	0x0400
+#define R02_PON_BIAS	0x0100
+#define R02_EN_AVC	0x0080
+#define R02_PON_AVC	0x0040
+#define R02_PON_LNA	0x0010
+#define R02_PON_PGAL	0x0008
+#define R02_PON_ADCL	0x0004
+#define R02_PON_PGAR	0x0002
+#define R02_PON_ADCR	0x0001
+#define R13_MTM		0x4000
+#define R14_SILENCE	0x0080
+#define R14_SDET_ON	0x0040
+#define R21_MT_ADC	0x8000
+#define R22_SEL_LNA	0x0008
+#define R22_SEL_MIC	0x0004
+#define R22_SKIP_DCFIL	0x0002
+#define R23_AGC_EN	0x0001
+
+struct uda1380_setup_data {
+	unsigned short i2c_address;
+	int            dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0
+#define UDA1380_DAC_CLK_WSPLL  1
+};
+
+#define UDA1380_DAI_DUPLEX	0 /* playback and capture on single DAI */
+#define UDA1380_DAI_PLAYBACK	1 /* playback DAI */
+#define UDA1380_DAI_CAPTURE	2 /* capture DAI */
+
+extern struct snd_soc_dai uda1380_dai[3];
+extern struct snd_soc_codec_device soc_codec_dev_uda1380;
+
+#endif /* _UDA1380_H */
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
new file mode 100644
index 0000000..67325fd
--- /dev/null
+++ b/sound/soc/codecs/wm8510.c
@@ -0,0 +1,817 @@
+/*
+ * wm8510.c  --  WM8510 ALSA Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8510.h"
+
+#define AUDIO_NAME "wm8510"
+#define WM8510_VERSION "0.6"
+
+struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+/*
+ * wm8510 register cache
+ * We can't read the WM8510 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0050, 0x0000, 0x0140, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00ff,
+	0x0000, 0x0000, 0x0100, 0x00ff,
+	0x0000, 0x0000, 0x012c, 0x002c,
+	0x002c, 0x002c, 0x002c, 0x0000,
+	0x0032, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0038, 0x000b, 0x0032, 0x0000,
+	0x0008, 0x000c, 0x0093, 0x00e9,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0003, 0x0010, 0x0000, 0x0000,
+	0x0000, 0x0002, 0x0001, 0x0000,
+	0x0000, 0x0000, 0x0039, 0x0000,
+	0x0001,
+};
+
+/*
+ * read wm8510 register cache
+ */
+static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg == WM8510_RESET)
+		return 0;
+	if (reg >= WM8510_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write wm8510 register cache
+ */
+static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= WM8510_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the WM8510 register space
+ */
+static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D9 WM8510 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8510_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+#define wm8510_reset(c)	wm8510_write(c, WM8510_RESET, 0)
+
+static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
+static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8510_alc[] = { "ALC", "Limiter" };
+
+static const struct soc_enum wm8510_enum[] = {
+	SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
+	SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
+	SOC_ENUM_SINGLE(WM8510_DAC,  4, 4, wm8510_deemp),
+	SOC_ENUM_SINGLE(WM8510_ALC3,  8, 2, wm8510_alc),
+};
+
+static const struct snd_kcontrol_new wm8510_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8510_enum[1]),
+SOC_ENUM("ADC Companding", wm8510_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
+
+SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
+
+SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
+
+SOC_SINGLE("Capture Volume", WM8510_ADCVOL,  0, 127, 0),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1,  8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1,  4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1,  0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2,  4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2,  0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8510_ALC1,  8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1,  3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1,  0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2,  8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8510_ALC2,  4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8510_ALC2,  0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
+SOC_SINGLE("ALC Capture Decay", WM8510_ALC3,  4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8510_ALC3,  0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE,  3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE,  0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA,  7, 1, 0),
+SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA,  0, 63, 0),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL,  7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL,  6, 1, 1),
+SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL,  0, 63, 0),
+SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST,  8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
+};
+
+/* add non dapm controls */
+static int wm8510_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8510_snd_controls[i], codec,
+					NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_boost_controls[] = {
+SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA,  6, 1, 0),
+SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),
+SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),
+};
+
+static const struct snd_kcontrol_new wm8510_micpga_controls[] = {
+SOC_DAPM_SINGLE("MICP Switch", WM8510_INPUT, 0, 1, 0),
+SOC_DAPM_SINGLE("MICN Switch", WM8510_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8510_INPUT, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
+	&wm8510_speaker_mixer_controls[0],
+	ARRAY_SIZE(wm8510_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
+	&wm8510_mono_mixer_controls[0],
+	ARRAY_SIZE(wm8510_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0,
+		 &wm8510_micpga_controls[0],
+		 ARRAY_SIZE(wm8510_micpga_controls)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0,
+	&wm8510_boost_controls[0],
+	ARRAY_SIZE(wm8510_boost_controls)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Mono output mixer */
+	{"Mono Mixer", "PCM Playback Switch", "DAC"},
+	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+	/* Speaker output mixer */
+	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
+	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+	/* Outputs */
+	{"Mono Out", NULL, "Mono Mixer"},
+	{"MONOOUT", NULL, "Mono Out"},
+	{"SpkN Out", NULL, "Speaker Mixer"},
+	{"SpkP Out", NULL, "Speaker Mixer"},
+	{"SPKOUTN", NULL, "SpkN Out"},
+	{"SPKOUTP", NULL, "SpkP Out"},
+
+	/* Microphone PGA */
+	{"Mic PGA", "MICN Switch", "MICN"},
+	{"Mic PGA", "MICP Switch", "MICP"},
+	{ "Mic PGA", "AUX Switch", "Aux Input" },
+
+	/* Boost Mixer */
+	{"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+	{"Boost Mixer", "Mic Volume", "MICP"},
+	{"Boost Mixer", "Aux Volume", "Aux Input"},
+
+	{"ADC", NULL, "Boost Mixer"},
+};
+
+static int wm8510_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8510_dapm_widgets,
+				  ARRAY_SIZE(wm8510_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+struct pll_ {
+	unsigned int pre_div:4; /* prescale - 1 */
+	unsigned int n:4;
+	unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+	unsigned long long Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div.pre_div = 1;
+		Ndiv = target / source;
+	} else
+		pll_div.pre_div = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+			"WM8510 N value %d outwith recommended range!d\n",
+			Ndiv);
+
+	pll_div.n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div.k = K;
+}
+
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	if (freq_in == 0 || freq_out == 0) {
+		/* Clock CODEC directly from MCLK */
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+		wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+
+		/* Turn off PLL */
+		reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+		wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
+		return 0;
+	}
+
+	pll_factors(freq_out*8, freq_in);
+
+	wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+	wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+	wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+	wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+	reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
+	wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+
+	/* Run CODEC from PLL instead of MCLK */
+	reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
+	wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
+
+	return 0;
+}
+
+/*
+ * Configure WM8510 clock dividers.
+ */
+static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+		int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	switch (div_id) {
+	case WM8510_OPCLKDIV:
+		reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
+		wm8510_write(codec, WM8510_GPIO, reg | div);
+		break;
+	case WM8510_MCLKDIV:
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
+		wm8510_write(codec, WM8510_CLOCK, reg | div);
+		break;
+	case WM8510_ADCCLK:
+		reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
+		wm8510_write(codec, WM8510_ADC, reg | div);
+		break;
+	case WM8510_DACCLK:
+		reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
+		wm8510_write(codec, WM8510_DAC, reg | div);
+		break;
+	case WM8510_BCLKDIV:
+		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
+		wm8510_write(codec, WM8510_CLOCK, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+	u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		clk |= 0x0001;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x0010;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0008;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x00018;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x0180;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0100;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0080;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8510_write(codec, WM8510_IFACE, iface);
+	wm8510_write(codec, WM8510_CLOCK, clk);
+	return 0;
+}
+
+static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
+	u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0020;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0040;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x0060;
+		break;
+	}
+
+	/* filter coefficient */
+	switch (params_rate(params)) {
+	case SNDRV_PCM_RATE_8000:
+		adn |= 0x5 << 1;
+		break;
+	case SNDRV_PCM_RATE_11025:
+		adn |= 0x4 << 1;
+		break;
+	case SNDRV_PCM_RATE_16000:
+		adn |= 0x3 << 1;
+		break;
+	case SNDRV_PCM_RATE_22050:
+		adn |= 0x2 << 1;
+		break;
+	case SNDRV_PCM_RATE_32000:
+		adn |= 0x1 << 1;
+		break;
+	case SNDRV_PCM_RATE_44100:
+	case SNDRV_PCM_RATE_48000:
+		break;
+	}
+
+	wm8510_write(codec, WM8510_IFACE, iface);
+	wm8510_write(codec, WM8510_ADD, adn);
+	return 0;
+}
+
+static int wm8510_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
+
+	if (mute)
+		wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
+	else
+		wm8510_write(codec, WM8510_DAC, mute_reg);
+	return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8510_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		wm8510_write(codec, WM8510_POWER1, 0x1ff);
+		wm8510_write(codec, WM8510_POWER2, 0x1ff);
+		wm8510_write(codec, WM8510_POWER3, 0x1ff);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* everything off, dac mute, inactive */
+		wm8510_write(codec, WM8510_POWER1, 0x0);
+		wm8510_write(codec, WM8510_POWER2, 0x0);
+		wm8510_write(codec, WM8510_POWER3, 0x0);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8510_dai = {
+	.name = "WM8510 HiFi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8510_RATES,
+		.formats = WM8510_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8510_RATES,
+		.formats = WM8510_FORMATS,},
+	.ops = {
+		.hw_params = wm8510_pcm_hw_params,
+	},
+	.dai_ops = {
+		.digital_mute = wm8510_mute,
+		.set_fmt = wm8510_set_dai_fmt,
+		.set_clkdiv = wm8510_set_dai_clkdiv,
+		.set_pll = wm8510_set_dai_pll,
+	},
+};
+EXPORT_SYMBOL_GPL(wm8510_dai);
+
+static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8510_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8510_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8510_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialise the WM8510 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8510_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "WM8510";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8510_read_reg_cache;
+	codec->write = wm8510_write;
+	codec->set_bias_level = wm8510_set_bias_level;
+	codec->dai = &wm8510_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(wm8510_reg);
+	codec->reg_cache = kmemdup(wm8510_reg, sizeof(wm8510_reg), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	wm8510_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8510: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* power on device */
+	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8510_add_controls(codec);
+	wm8510_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8510: failed to register card\n");
+		goto card_err;
+	}
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static struct snd_soc_device *wm8510_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8510 2 wire address is 0x1a
+ */
+#define I2C_DRIVERID_WM8510 0xfefe /* liam -  need a proper id */
+
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8510_i2c_driver;
+static struct i2c_client client_template;
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+   around */
+
+static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct snd_soc_device *socdev = wm8510_socdev;
+	struct wm8510_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c;
+	int ret;
+
+	if (addr != setup->i2c_address)
+		return -ENODEV;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		pr_err("failed to attach codec at addr %x\n", addr);
+		goto err;
+	}
+
+	ret = wm8510_init(socdev);
+	if (ret < 0) {
+		pr_err("failed to initialise WM8510\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int wm8510_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	i2c_detach_client(client);
+	kfree(codec->reg_cache);
+	kfree(client);
+	return 0;
+}
+
+static int wm8510_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8510_codec_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver wm8510_i2c_driver = {
+	.driver = {
+		.name = "WM8510 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_WM8510,
+	.attach_adapter = wm8510_i2c_attach,
+	.detach_client =  wm8510_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8510",
+	.driver = &wm8510_i2c_driver,
+};
+#endif
+
+static int wm8510_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8510_setup_data *setup;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	pr_info("WM8510 Audio Codec %s", WM8510_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	wm8510_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		normal_i2c[0] = setup->i2c_address;
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = i2c_add_driver(&wm8510_i2c_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add i2c driver");
+	}
+#else
+	/* Add other interfaces here */
+#endif
+	return ret;
+}
+
+/* power down chip */
+static int wm8510_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8510_i2c_driver);
+#endif
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8510 = {
+	.probe = 	wm8510_probe,
+	.remove = 	wm8510_remove,
+	.suspend = 	wm8510_suspend,
+	.resume =	wm8510_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
+
+MODULE_DESCRIPTION("ASoC WM8510 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h
new file mode 100644
index 0000000..f5d2e42
--- /dev/null
+++ b/sound/soc/codecs/wm8510.h
@@ -0,0 +1,103 @@
+/*
+ * wm8510.h  --  WM8510 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8510_H
+#define _WM8510_H
+
+/* WM8510 register space */
+
+#define WM8510_RESET		0x0
+#define WM8510_POWER1		0x1
+#define WM8510_POWER2		0x2
+#define WM8510_POWER3		0x3
+#define WM8510_IFACE		0x4
+#define WM8510_COMP			0x5
+#define WM8510_CLOCK		0x6
+#define WM8510_ADD			0x7
+#define WM8510_GPIO			0x8
+#define WM8510_DAC			0xa
+#define WM8510_DACVOL		0xb
+#define WM8510_ADC			0xe
+#define WM8510_ADCVOL		0xf
+#define WM8510_EQ1			0x12
+#define WM8510_EQ2			0x13
+#define WM8510_EQ3			0x14
+#define WM8510_EQ4			0x15
+#define WM8510_EQ5			0x16
+#define WM8510_DACLIM1		0x18
+#define WM8510_DACLIM2		0x19
+#define WM8510_NOTCH1		0x1b
+#define WM8510_NOTCH2		0x1c
+#define WM8510_NOTCH3		0x1d
+#define WM8510_NOTCH4		0x1e
+#define WM8510_ALC1			0x20
+#define WM8510_ALC2			0x21
+#define WM8510_ALC3			0x22
+#define WM8510_NGATE		0x23
+#define WM8510_PLLN			0x24
+#define WM8510_PLLK1		0x25
+#define WM8510_PLLK2		0x26
+#define WM8510_PLLK3		0x27
+#define WM8510_ATTEN		0x28
+#define WM8510_INPUT		0x2c
+#define WM8510_INPPGA		0x2d
+#define WM8510_ADCBOOST		0x2f
+#define WM8510_OUTPUT		0x31
+#define WM8510_SPKMIX		0x32
+#define WM8510_SPKVOL		0x36
+#define WM8510_MONOMIX		0x38
+
+#define WM8510_CACHEREGNUM 	57
+
+/* Clock divider Id's */
+#define WM8510_OPCLKDIV		0
+#define WM8510_MCLKDIV		1
+#define WM8510_ADCCLK		2
+#define WM8510_DACCLK		3
+#define WM8510_BCLKDIV		4
+
+/* DAC clock dividers */
+#define WM8510_DACCLK_F2	(1 << 3)
+#define WM8510_DACCLK_F4	(0 << 3)
+
+/* ADC clock dividers */
+#define WM8510_ADCCLK_F2	(1 << 3)
+#define WM8510_ADCCLK_F4	(0 << 3)
+
+/* PLL Out dividers */
+#define WM8510_OPCLKDIV_1	(0 << 4)
+#define WM8510_OPCLKDIV_2	(1 << 4)
+#define WM8510_OPCLKDIV_3	(2 << 4)
+#define WM8510_OPCLKDIV_4	(3 << 4)
+
+/* BCLK clock dividers */
+#define WM8510_BCLKDIV_1	(0 << 2)
+#define WM8510_BCLKDIV_2	(1 << 2)
+#define WM8510_BCLKDIV_4	(2 << 2)
+#define WM8510_BCLKDIV_8	(3 << 2)
+#define WM8510_BCLKDIV_16	(4 << 2)
+#define WM8510_BCLKDIV_32	(5 << 2)
+
+/* MCLK clock dividers */
+#define WM8510_MCLKDIV_1	(0 << 5)
+#define WM8510_MCLKDIV_1_5	(1 << 5)
+#define WM8510_MCLKDIV_2	(2 << 5)
+#define WM8510_MCLKDIV_3	(3 << 5)
+#define WM8510_MCLKDIV_4	(4 << 5)
+#define WM8510_MCLKDIV_6	(5 << 5)
+#define WM8510_MCLKDIV_8	(6 << 5)
+#define WM8510_MCLKDIV_12	(7 << 5)
+
+struct wm8510_setup_data {
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8510_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8510;
+
+#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 0cf9265..369d39c 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -31,25 +31,6 @@
 #define AUDIO_NAME "wm8731"
 #define WM8731_VERSION "0.13"
 
-/*
- * Debug
- */
-
-#define WM8731_DEBUG 0
-
-#ifdef WM8731_DEBUG
-#define dbg(format, arg...) \
-	printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
-	printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
-	printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
-	printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
 struct snd_soc_codec_device soc_codec_dev_wm8731;
 
 /* codec private data */
@@ -193,7 +174,7 @@
 SND_SOC_DAPM_INPUT("LLINEIN"),
 };
 
-static const char *intercon[][3] = {
+static const struct snd_soc_dapm_route intercon[] = {
 	/* output mixer */
 	{"Output Mixer", "Line Bypass Switch", "Line Input"},
 	{"Output Mixer", "HiFi Playback Switch", "DAC"},
@@ -214,22 +195,14 @@
 	{"Line Input", NULL, "LLINEIN"},
 	{"Line Input", NULL, "RLINEIN"},
 	{"Mic Bias", NULL, "MICIN"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 static int wm8731_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
+	snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+				  ARRAY_SIZE(wm8731_dapm_widgets));
 
-	for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
-
-	/* set up audio path interconnects */
-	for (i = 0; intercon[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, intercon[i][0],
-			intercon[i][1], intercon[i][2]);
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -345,7 +318,7 @@
 	}
 }
 
-static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8731_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
@@ -357,7 +330,7 @@
 	return 0;
 }
 
-static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -376,7 +349,7 @@
 }
 
 
-static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -435,29 +408,29 @@
 	return 0;
 }
 
-static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8731_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* vref/mid, osc on, dac unmute */
 		wm8731_write(codec, WM8731_PWR, reg);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* everything off except vref/vmid, */
 		wm8731_write(codec, WM8731_PWR, reg | 0x0040);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		/* everything off, dac mute, inactive */
 		wm8731_write(codec, WM8731_ACTIVE, 0x0);
 		wm8731_write(codec, WM8731_PWR, 0xffff);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -470,7 +443,7 @@
 #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 	SNDRV_PCM_FMTBIT_S24_LE)
 
-struct snd_soc_codec_dai wm8731_dai = {
+struct snd_soc_dai wm8731_dai = {
 	.name = "WM8731",
 	.playback = {
 		.stream_name = "Playback",
@@ -503,7 +476,7 @@
 	struct snd_soc_codec *codec = socdev->codec;
 
 	wm8731_write(codec, WM8731_ACTIVE, 0x0);
-	wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -521,8 +494,8 @@
 		data[1] = cache[i] & 0x00ff;
 		codec->hw_write(codec->control_data, data, 2);
 	}
-	wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-	wm8731_dapm_event(codec, codec->suspend_dapm_state);
+	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8731_set_bias_level(codec, codec->suspend_bias_level);
 	return 0;
 }
 
@@ -539,10 +512,10 @@
 	codec->owner = THIS_MODULE;
 	codec->read = wm8731_read_reg_cache;
 	codec->write = wm8731_write;
-	codec->dapm_event = wm8731_dapm_event;
+	codec->set_bias_level = wm8731_set_bias_level;
 	codec->dai = &wm8731_dai;
 	codec->num_dai = 1;
-	codec->reg_cache_size = sizeof(wm8731_reg);
+	codec->reg_cache_size = ARRAY_SIZE(wm8731_reg);
 	codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
@@ -557,7 +530,7 @@
 	}
 
 	/* power on device */
-	wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* set the update bits */
 	reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
@@ -632,13 +605,13 @@
 
 	ret = i2c_attach_client(i2c);
 	if (ret < 0) {
-		err("failed to attach codec at addr %x\n", addr);
+		pr_err("failed to attach codec at addr %x\n", addr);
 		goto err;
 	}
 
 	ret = wm8731_init(socdev);
 	if (ret < 0) {
-		err("failed to initialise WM8731\n");
+		pr_err("failed to initialise WM8731\n");
 		goto err;
 	}
 	return ret;
@@ -689,7 +662,7 @@
 	struct wm8731_priv *wm8731;
 	int ret = 0;
 
-	info("WM8731 Audio Codec %s", WM8731_VERSION);
+	pr_info("WM8731 Audio Codec %s", WM8731_VERSION);
 
 	setup = socdev->codec_data;
 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -730,7 +703,7 @@
 	struct snd_soc_codec *codec = socdev->codec;
 
 	if (codec->control_data)
-		wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+		wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index 5bcab6a..99f2e3c 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -38,7 +38,7 @@
 	unsigned short i2c_address;
 };
 
-extern struct snd_soc_codec_dai wm8731_dai;
+extern struct snd_soc_dai wm8731_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8731;
 
 #endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 16cd5d4..e23cb09 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -31,25 +31,6 @@
 #define AUDIO_NAME "WM8750"
 #define WM8750_VERSION "0.12"
 
-/*
- * Debug
- */
-
-#define WM8750_DEBUG 0
-
-#ifdef WM8750_DEBUG
-#define dbg(format, arg...) \
-	printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
-	printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
-	printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
-	printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
 /* codec private data */
 struct wm8750_priv {
 	unsigned int sysclk;
@@ -378,7 +359,7 @@
 	SND_SOC_DAPM_INPUT("RINPUT3"),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* left mixer */
 	{"Left Mixer", "Playback Switch", "Left DAC"},
 	{"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
@@ -470,22 +451,14 @@
 	/* ADC */
 	{"Left ADC", NULL, "Left ADC Mux"},
 	{"Right ADC", NULL, "Right ADC Mux"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 static int wm8750_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
+	snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+				  ARRAY_SIZE(wm8750_dapm_widgets));
 
-	for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
-
-	/* set up audio path audio_mapnects */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -563,7 +536,7 @@
 	return -EINVAL;
 }
 
-static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -581,7 +554,7 @@
 	return -EINVAL;
 }
 
-static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -674,7 +647,7 @@
 	return 0;
 }
 
-static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8750_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
@@ -686,29 +659,29 @@
 	return 0;
 }
 
-static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8750_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* set vmid to 50k and unmute dac */
 		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		/* set vmid to 5k for quick power up */
 		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* mute dac and set vmid to 500k, enable VREF */
 		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		wm8750_write(codec, WM8750_PWR1, 0x0001);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -719,7 +692,7 @@
 #define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 	SNDRV_PCM_FMTBIT_S24_LE)
 
-struct snd_soc_codec_dai wm8750_dai = {
+struct snd_soc_dai wm8750_dai = {
 	.name = "WM8750",
 	.playback = {
 		.stream_name = "Playback",
@@ -748,7 +721,7 @@
 {
 	struct snd_soc_codec *codec =
 		container_of(work, struct snd_soc_codec, delayed_work.work);
-	wm8750_dapm_event(codec, codec->dapm_state);
+	wm8750_set_bias_level(codec, codec->bias_level);
 }
 
 static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
@@ -756,7 +729,7 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->codec;
 
-	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -777,12 +750,12 @@
 		codec->hw_write(codec->control_data, data, 2);
 	}
 
-	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* charge wm8750 caps */
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
-		wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
-		codec->dapm_state = SNDRV_CTL_POWER_D0;
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+		wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+		codec->bias_level = SND_SOC_BIAS_ON;
 		schedule_delayed_work(&codec->delayed_work,
 					msecs_to_jiffies(1000));
 	}
@@ -803,10 +776,10 @@
 	codec->owner = THIS_MODULE;
 	codec->read = wm8750_read_reg_cache;
 	codec->write = wm8750_write;
-	codec->dapm_event = wm8750_dapm_event;
+	codec->set_bias_level = wm8750_set_bias_level;
 	codec->dai = &wm8750_dai;
 	codec->num_dai = 1;
-	codec->reg_cache_size = sizeof(wm8750_reg);
+	codec->reg_cache_size = ARRAY_SIZE(wm8750_reg);
 	codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
@@ -821,8 +794,8 @@
 	}
 
 	/* charge output caps */
-	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
-	codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+	wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+	codec->bias_level = SND_SOC_BIAS_STANDBY;
 	schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
 
 	/* set the update bits */
@@ -904,13 +877,13 @@
 
 	ret = i2c_attach_client(i2c);
 	if (ret < 0) {
-		err("failed to attach codec at addr %x\n", addr);
+		pr_err("failed to attach codec at addr %x\n", addr);
 		goto err;
 	}
 
 	ret = wm8750_init(socdev);
 	if (ret < 0) {
-	err("failed to initialise WM8750\n");
+		pr_err("failed to initialise WM8750\n");
 		goto err;
 	}
 	return ret;
@@ -961,7 +934,7 @@
 	struct wm8750_priv *wm8750;
 	int ret = 0;
 
-	info("WM8750 Audio Codec %s", WM8750_VERSION);
+	pr_info("WM8750 Audio Codec %s", WM8750_VERSION);
 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
 	if (codec == NULL)
 		return -ENOMEM;
@@ -1021,7 +994,7 @@
 	struct snd_soc_codec *codec = socdev->codec;
 
 	if (codec->control_data)
-		wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+		wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	run_delayed_work(&codec->delayed_work);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
index a97a54a..8ef30e6 100644
--- a/sound/soc/codecs/wm8750.h
+++ b/sound/soc/codecs/wm8750.h
@@ -61,7 +61,7 @@
 	unsigned short i2c_address;
 };
 
-extern struct snd_soc_codec_dai wm8750_dai;
+extern struct snd_soc_dai wm8750_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8750;
 
 #endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index fb41826..8604809 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -55,25 +55,6 @@
 #define AUDIO_NAME "wm8753"
 #define WM8753_VERSION "0.16"
 
-/*
- * Debug
- */
-
-#define WM8753_DEBUG 0
-
-#ifdef WM8753_DEBUG
-#define dbg(format, arg...) \
-	printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-#else
-#define dbg(format, arg...) do {} while (0)
-#endif
-#define err(format, arg...) \
-	printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-#define info(format, arg...) \
-	printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-#define warn(format, arg...) \
-	printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-
 static int caps_charge = 2000;
 module_param(caps_charge, int, 0);
 MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
@@ -260,28 +241,50 @@
 	return 1;
 }
 
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const unsigned int out_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	/* 0000000 - 0101111 = "Analogue mute" */
+	0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0),
+	48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0),
+};
+static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
 
 static const struct snd_kcontrol_new wm8753_snd_controls[] = {
-SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
+SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv),
 
-SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0),
+SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0,
+		 adc_tlv),
 
-SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),
-SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V,
+		 0, 127, 0, out_tlv),
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0,
+		 127, 0, out_tlv),
 
-SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),
+SOC_SINGLE_TLV("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0, out_tlv),
 
-SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),
-SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),
-SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),
+SOC_DOUBLE_R_TLV("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7,
+		 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4,
+		 7, 1, mix_tlv),
+SOC_DOUBLE_R_TLV("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7,
+		 1, voice_mix_tlv),
 
-SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),
-SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7,
+	     1, 0),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7,
+	     1, 0),
 
-SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
-SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
-SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1),
+SOC_SINGLE_TLV("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1, mix_tlv),
+SOC_SINGLE_TLV("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1,
+	       mix_tlv),
+SOC_SINGLE_TLV("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1,
+	       voice_mix_tlv),
 SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
 
 SOC_ENUM("Bass Boost", wm8753_enum[0]),
@@ -291,10 +294,13 @@
 SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
 SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
 
-SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv),
-SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv),
+SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1,
+	       rec_mix_tlv),
+SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1,
+	       rec_mix_tlv),
 
-SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0,
+		 pga_tlv),
 SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
 SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),
 
@@ -326,8 +332,8 @@
 SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
 SOC_ENUM("Playback Phase", wm8753_enum[10]),
 
-SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
-SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
+SOC_SINGLE_TLV("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0, mic_preamp_tlv),
+SOC_SINGLE_TLV("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0, mic_preamp_tlv),
 
 SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
 
@@ -523,7 +529,7 @@
 SND_SOC_DAPM_VMID("VREF"),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* left mixer */
 	{"Left Mixer", "Left Playback Switch", "Left DAC"},
 	{"Left Mixer", "Voice Playback Switch", "Voice DAC"},
@@ -674,23 +680,14 @@
 
 	/* ACOP */
 	{"ACOP", NULL, "ALC Mixer"},
-
-	/* terminator */
-	{NULL, NULL, NULL},
 };
 
 static int wm8753_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
+	snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+				  ARRAY_SIZE(wm8753_dapm_widgets));
 
-	for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
-
-	/* set up the WM8753 audio map */
-	for (i = 0; audio_map[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
-	}
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -743,7 +740,7 @@
 	pll_div->k = K;
 }
 
-static int wm8753_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
 		int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
 	u16 reg, enable;
@@ -866,7 +863,7 @@
 /*
  * Clock after PLL and dividers
  */
-static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -893,7 +890,7 @@
 /*
  * Set's ADC and Voice DAC format.
  */
-static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -963,7 +960,7 @@
 /*
  * Set's PCM dai fmt and BCLK.
  */
-static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1029,7 +1026,7 @@
 	return 0;
 }
 
-static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 		int div_id, int div)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1057,7 +1054,7 @@
 /*
  * Set's HiFi DAC format.
  */
-static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1090,7 +1087,7 @@
 /*
  * Set's I2S DAI format.
  */
-static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1198,7 +1195,7 @@
 	return 0;
 }
 
-static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1213,7 +1210,7 @@
 	return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
@@ -1221,7 +1218,7 @@
 	return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1236,7 +1233,7 @@
 	return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1253,7 +1250,7 @@
 	return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
 }
 
-static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
+static int wm8753_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
 	u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7;
@@ -1274,29 +1271,29 @@
 	return 0;
 }
 
-static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm8753_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* set vmid to 50k and unmute dac */
 		wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		/* set vmid to 5k for quick power up */
 		wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* mute dac and set vmid to 500k, enable VREF */
 		wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		wm8753_write(codec, WM8753_PWR1, 0x0001);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -1319,7 +1316,7 @@
  * 3. Voice disabled - HIFI over HIFI
  * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
  */
-static const struct snd_soc_codec_dai wm8753_all_dai[] = {
+static const struct snd_soc_dai wm8753_all_dai[] = {
 /* DAI HiFi mode 1 */
 {	.name = "WM8753 HiFi",
 	.id = 1,
@@ -1459,7 +1456,7 @@
 },
 };
 
-struct snd_soc_codec_dai wm8753_dai[2];
+struct snd_soc_dai wm8753_dai[2];
 EXPORT_SYMBOL_GPL(wm8753_dai);
 
 static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
@@ -1500,7 +1497,7 @@
 {
 	struct snd_soc_codec *codec =
 		container_of(work, struct snd_soc_codec, delayed_work.work);
-	wm8753_dapm_event(codec, codec->dapm_state);
+	wm8753_set_bias_level(codec, codec->bias_level);
 }
 
 static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
@@ -1512,7 +1509,7 @@
 	if (!codec->card)
 		return 0;
 
-	wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -1537,12 +1534,12 @@
 		codec->hw_write(codec->control_data, data, 2);
 	}
 
-	wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* charge wm8753 caps */
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
-		wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
-		codec->dapm_state = SNDRV_CTL_POWER_D0;
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+		wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+		codec->bias_level = SND_SOC_BIAS_ON;
 		schedule_delayed_work(&codec->delayed_work,
 			msecs_to_jiffies(caps_charge));
 	}
@@ -1563,10 +1560,10 @@
 	codec->owner = THIS_MODULE;
 	codec->read = wm8753_read_reg_cache;
 	codec->write = wm8753_write;
-	codec->dapm_event = wm8753_dapm_event;
+	codec->set_bias_level = wm8753_set_bias_level;
 	codec->dai = wm8753_dai;
 	codec->num_dai = 2;
-	codec->reg_cache_size = sizeof(wm8753_reg);
+	codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
 	codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL);
 
 	if (codec->reg_cache == NULL)
@@ -1584,8 +1581,8 @@
 	}
 
 	/* charge output caps */
-	wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
-	codec->dapm_state = SNDRV_CTL_POWER_D3hot;
+	wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+	codec->bias_level = SND_SOC_BIAS_STANDBY;
 	schedule_delayed_work(&codec->delayed_work,
 		msecs_to_jiffies(caps_charge));
 
@@ -1673,13 +1670,13 @@
 
 	ret = i2c_attach_client(i2c);
 	if (ret < 0) {
-		err("failed to attach codec at addr %x\n", addr);
+		pr_err("failed to attach codec at addr %x\n", addr);
 		goto err;
 	}
 
 	ret = wm8753_init(socdev);
 	if (ret < 0) {
-		err("failed to initialise WM8753\n");
+		pr_err("failed to initialise WM8753\n");
 		goto err;
 	}
 
@@ -1731,7 +1728,7 @@
 	struct wm8753_priv *wm8753;
 	int ret = 0;
 
-	info("WM8753 Audio Codec %s", WM8753_VERSION);
+	pr_info("WM8753 Audio Codec %s", WM8753_VERSION);
 
 	setup = socdev->codec_data;
 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -1792,7 +1789,7 @@
 	struct snd_soc_codec *codec = socdev->codec;
 
 	if (codec->control_data)
-		wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+		wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	run_delayed_work(&codec->delayed_work);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index 95e2a1f..44f5f1f 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -120,7 +120,7 @@
 #define WM8753_DAI_HIFI		0
 #define WM8753_DAI_VOICE		1
 
-extern struct snd_soc_codec_dai wm8753_dai[2];
+extern struct snd_soc_dai wm8753_dai[2];
 extern struct snd_soc_codec_device soc_codec_dev_wm8753;
 
 #endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
new file mode 100644
index 0000000..3ecce51
--- /dev/null
+++ b/sound/soc/codecs/wm8990.c
@@ -0,0 +1,1626 @@
+/*
+ * wm8990.c  --  WM8990 ALSA Soc Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         lg@opensource.wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8990.h"
+
+#define AUDIO_NAME "wm8990"
+#define WM8990_VERSION "0.2"
+
+/* codec private data */
+struct wm8990_priv {
+	unsigned int sysclk;
+	unsigned int pcmclk;
+};
+
+/*
+ * wm8990 register cache.  Note that register 0 is not included in the
+ * cache.
+ */
+static const u16 wm8990_reg[] = {
+	0x8990,     /* R0  - Reset */
+	0x0000,     /* R1  - Power Management (1) */
+	0x6000,     /* R2  - Power Management (2) */
+	0x0000,     /* R3  - Power Management (3) */
+	0x4050,     /* R4  - Audio Interface (1) */
+	0x4000,     /* R5  - Audio Interface (2) */
+	0x01C8,     /* R6  - Clocking (1) */
+	0x0000,     /* R7  - Clocking (2) */
+	0x0040,     /* R8  - Audio Interface (3) */
+	0x0040,     /* R9  - Audio Interface (4) */
+	0x0004,     /* R10 - DAC CTRL */
+	0x00C0,     /* R11 - Left DAC Digital Volume */
+	0x00C0,     /* R12 - Right DAC Digital Volume */
+	0x0000,     /* R13 - Digital Side Tone */
+	0x0100,     /* R14 - ADC CTRL */
+	0x00C0,     /* R15 - Left ADC Digital Volume */
+	0x00C0,     /* R16 - Right ADC Digital Volume */
+	0x0000,     /* R17 */
+	0x0000,     /* R18 - GPIO CTRL 1 */
+	0x1000,     /* R19 - GPIO1 & GPIO2 */
+	0x1010,     /* R20 - GPIO3 & GPIO4 */
+	0x1010,     /* R21 - GPIO5 & GPIO6 */
+	0x8000,     /* R22 - GPIOCTRL 2 */
+	0x0800,     /* R23 - GPIO_POL */
+	0x008B,     /* R24 - Left Line Input 1&2 Volume */
+	0x008B,     /* R25 - Left Line Input 3&4 Volume */
+	0x008B,     /* R26 - Right Line Input 1&2 Volume */
+	0x008B,     /* R27 - Right Line Input 3&4 Volume */
+	0x0000,     /* R28 - Left Output Volume */
+	0x0000,     /* R29 - Right Output Volume */
+	0x0066,     /* R30 - Line Outputs Volume */
+	0x0022,     /* R31 - Out3/4 Volume */
+	0x0079,     /* R32 - Left OPGA Volume */
+	0x0079,     /* R33 - Right OPGA Volume */
+	0x0003,     /* R34 - Speaker Volume */
+	0x0003,     /* R35 - ClassD1 */
+	0x0000,     /* R36 */
+	0x0100,     /* R37 - ClassD3 */
+	0x0000,     /* R38 */
+	0x0000,     /* R39 - Input Mixer1 */
+	0x0000,     /* R40 - Input Mixer2 */
+	0x0000,     /* R41 - Input Mixer3 */
+	0x0000,     /* R42 - Input Mixer4 */
+	0x0000,     /* R43 - Input Mixer5 */
+	0x0000,     /* R44 - Input Mixer6 */
+	0x0000,     /* R45 - Output Mixer1 */
+	0x0000,     /* R46 - Output Mixer2 */
+	0x0000,     /* R47 - Output Mixer3 */
+	0x0000,     /* R48 - Output Mixer4 */
+	0x0000,     /* R49 - Output Mixer5 */
+	0x0000,     /* R50 - Output Mixer6 */
+	0x0180,     /* R51 - Out3/4 Mixer */
+	0x0000,     /* R52 - Line Mixer1 */
+	0x0000,     /* R53 - Line Mixer2 */
+	0x0000,     /* R54 - Speaker Mixer */
+	0x0000,     /* R55 - Additional Control */
+	0x0000,     /* R56 - AntiPOP1 */
+	0x0000,     /* R57 - AntiPOP2 */
+	0x0000,     /* R58 - MICBIAS */
+	0x0000,     /* R59 */
+	0x0008,     /* R60 - PLL1 */
+	0x0031,     /* R61 - PLL2 */
+	0x0026,     /* R62 - PLL3 */
+};
+
+/*
+ * read wm8990 register cache
+ */
+static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+	return cache[reg];
+}
+
+/*
+ * write wm8990 register cache
+ */
+static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
+
+	/* Reset register is uncached */
+	if (reg == 0)
+		return;
+
+	cache[reg] = value;
+}
+
+/*
+ * write to the wm8990 register space
+ */
+static int wm8990_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[3];
+
+	data[0] = reg & 0xFF;
+	data[1] = (value >> 8) & 0xFF;
+	data[2] = value & 0xFF;
+
+	wm8990_write_reg_cache(codec, reg, value);
+
+	if (codec->hw_write(codec->control_data, data, 3) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+#define wm8990_reset(c) wm8990_write(c, WM8990_RESET, 0)
+
+static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+
+static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+
+static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
+
+static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+
+static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+
+static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+
+static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+
+static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int ret;
+	u16 val;
+
+	ret = snd_soc_put_volsw(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	/* now hit the volume update bits (always bit 8) */
+	val = wm8990_read_reg_cache(codec, reg);
+	return wm8990_write(codec, reg, val | 0x0100);
+}
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+	 tlv_array) {\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		  SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
+	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+
+static const char *wm8990_digital_sidetone[] =
+	{"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8990_left_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADC_TO_DACL_SHIFT,
+	WM8990_ADC_TO_DACL_MASK,
+	wm8990_digital_sidetone);
+
+static const struct soc_enum wm8990_right_digital_sidetone_enum =
+SOC_ENUM_SINGLE(WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADC_TO_DACR_SHIFT,
+	WM8990_ADC_TO_DACR_MASK,
+	wm8990_digital_sidetone);
+
+static const char *wm8990_adcmode[] =
+	{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8990_right_adcmode_enum =
+SOC_ENUM_SINGLE(WM8990_ADC_CTRL,
+	WM8990_ADC_HPF_CUT_SHIFT,
+	WM8990_ADC_HPF_CUT_MASK,
+	wm8990_adcmode);
+
+static const struct snd_kcontrol_new wm8990_snd_controls[] = {
+/* INMIXL */
+SOC_SINGLE("LIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L12MNBST_BIT, 1, 0),
+SOC_SINGLE("LIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L34MNBST_BIT, 1, 0),
+/* INMIXR */
+SOC_SINGLE("RIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R12MNBST_BIT, 1, 0),
+SOC_SINGLE("RIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R34MNBST_BIT, 1, 0),
+
+/* LOMIX */
+SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER3,
+	WM8990_LLI3LOVOL_SHIFT, WM8990_LLI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+	WM8990_LR12LOVOL_SHIFT, WM8990_LR12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3,
+	WM8990_LL12LOVOL_SHIFT, WM8990_LL12LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER5,
+	WM8990_LRI3LOVOL_SHIFT, WM8990_LRI3LOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+	WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER5,
+	WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+/* ROMIX */
+SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER4,
+	WM8990_RRI3ROVOL_SHIFT, WM8990_RRI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+	WM8990_RL12ROVOL_SHIFT, WM8990_RL12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4,
+	WM8990_RR12ROVOL_SHIFT, WM8990_RR12ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER6,
+	WM8990_RLI3ROVOL_SHIFT, WM8990_RLI3ROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+	WM8990_RLBROVOL_SHIFT, WM8990_RLBROVOL_MASK, 1, out_mix_tlv),
+SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER6,
+	WM8990_RRBROVOL_SHIFT, WM8990_RRBROVOL_MASK, 1, out_mix_tlv),
+
+/* LOUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8990_LEFT_OUTPUT_VOLUME,
+	WM8990_LOUTVOL_SHIFT, WM8990_LOUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOUT ZC", WM8990_LEFT_OUTPUT_VOLUME, WM8990_LOZC_BIT, 1, 0),
+
+/* ROUT */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8990_RIGHT_OUTPUT_VOLUME,
+	WM8990_ROUTVOL_SHIFT, WM8990_ROUTVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROUT ZC", WM8990_RIGHT_OUTPUT_VOLUME, WM8990_ROZC_BIT, 1, 0),
+
+/* LOPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8990_LEFT_OPGA_VOLUME,
+	WM8990_LOPGAVOL_SHIFT, WM8990_LOPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("LOPGA ZC Switch", WM8990_LEFT_OPGA_VOLUME,
+	WM8990_LOPGAZC_BIT, 1, 0),
+
+/* ROPGA */
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8990_RIGHT_OPGA_VOLUME,
+	WM8990_ROPGAVOL_SHIFT, WM8990_ROPGAVOL_MASK, 0, out_pga_tlv),
+SOC_SINGLE("ROPGA ZC Switch", WM8990_RIGHT_OPGA_VOLUME,
+	WM8990_ROPGAZC_BIT, 1, 0),
+
+SOC_SINGLE("LON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_LONMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_LOPMUTE_BIT, 1, 0),
+SOC_SINGLE("LOP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_LOATTN_BIT, 1, 0),
+SOC_SINGLE("RON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_RONMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_ROPMUTE_BIT, 1, 0),
+SOC_SINGLE("ROP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME,
+	WM8990_ROATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT3 Mute Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT3MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT3 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT3ATTN_BIT, 1, 0),
+
+SOC_SINGLE("OUT4 Mute Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT4MUTE_BIT, 1, 0),
+SOC_SINGLE("OUT4 Attenuation Switch", WM8990_OUT3_4_VOLUME,
+	WM8990_OUT4ATTN_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Mode Switch", WM8990_CLASSD1,
+	WM8990_CDMODE_BIT, 1, 0),
+
+SOC_SINGLE("Speaker Output Attenuation Volume", WM8990_SPEAKER_VOLUME,
+	WM8990_SPKVOL_SHIFT, WM8990_SPKVOL_MASK, 0),
+SOC_SINGLE("Speaker DC Boost Volume", WM8990_CLASSD3,
+	WM8990_DCGAIN_SHIFT, WM8990_DCGAIN_MASK, 0),
+SOC_SINGLE("Speaker AC Boost Volume", WM8990_CLASSD3,
+	WM8990_ACGAIN_SHIFT, WM8990_ACGAIN_MASK, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+	WM8990_LEFT_DAC_DIGITAL_VOLUME,
+	WM8990_DACL_VOL_SHIFT,
+	WM8990_DACL_VOL_MASK,
+	0,
+	out_dac_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+	WM8990_RIGHT_DAC_DIGITAL_VOLUME,
+	WM8990_DACR_VOL_SHIFT,
+	WM8990_DACR_VOL_MASK,
+	0,
+	out_dac_tlv),
+
+SOC_ENUM("Left Digital Sidetone", wm8990_left_digital_sidetone_enum),
+SOC_ENUM("Right Digital Sidetone", wm8990_right_digital_sidetone_enum),
+
+SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADCL_DAC_SVOL_SHIFT, WM8990_ADCL_DAC_SVOL_MASK, 0,
+	out_sidetone_tlv),
+SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE,
+	WM8990_ADCR_DAC_SVOL_SHIFT, WM8990_ADCR_DAC_SVOL_MASK, 0,
+	out_sidetone_tlv),
+
+SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8990_ADC_CTRL,
+	WM8990_ADC_HPF_ENA_BIT, 1, 0),
+
+SOC_ENUM("ADC HPF Mode", wm8990_right_adcmode_enum),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+	WM8990_LEFT_ADC_DIGITAL_VOLUME,
+	WM8990_ADCL_VOL_SHIFT,
+	WM8990_ADCL_VOL_MASK,
+	0,
+	in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+	WM8990_RIGHT_ADC_DIGITAL_VOLUME,
+	WM8990_ADCR_VOL_SHIFT,
+	WM8990_ADCR_VOL_MASK,
+	0,
+	in_adc_tlv),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+	WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+	WM8990_LIN12VOL_SHIFT,
+	WM8990_LIN12VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("LIN12 ZC Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+	WM8990_LI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN12 Mute Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME,
+	WM8990_LI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+	WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+	WM8990_LIN34VOL_SHIFT,
+	WM8990_LIN34VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("LIN34 ZC Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+	WM8990_LI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("LIN34 Mute Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME,
+	WM8990_LI34MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+	WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+	WM8990_RIN12VOL_SHIFT,
+	WM8990_RIN12VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("RIN12 ZC Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+	WM8990_RI12ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN12 Mute Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME,
+	WM8990_RI12MUTE_BIT, 1, 0),
+
+SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+	WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+	WM8990_RIN34VOL_SHIFT,
+	WM8990_RIN34VOL_MASK,
+	0,
+	in_pga_tlv),
+
+SOC_SINGLE("RIN34 ZC Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+	WM8990_RI34ZC_BIT, 1, 0),
+
+SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
+	WM8990_RI34MUTE_BIT, 1, 0),
+
+};
+
+/* add non dapm controls */
+static int wm8990_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8990_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8990_snd_controls[i], codec,
+					NULL));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ * _DAPM_ Controls
+ */
+
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	u16 reg, fakepower;
+
+	reg = wm8990_read_reg_cache(w->codec, WM8990_POWER_MANAGEMENT_2);
+	fakepower = wm8990_read_reg_cache(w->codec, WM8990_INTDRIVBITS);
+
+	if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) |
+		(1 << WM8990_AINLMUX_PWR_BIT))) {
+		reg |= WM8990_AINL_ENA;
+	} else {
+		reg &= ~WM8990_AINL_ENA;
+	}
+
+	if (fakepower & ((1 << WM8990_INMIXR_PWR_BIT) |
+		(1 << WM8990_AINRMUX_PWR_BIT))) {
+		reg |= WM8990_AINR_ENA;
+	} else {
+		reg &= ~WM8990_AINL_ENA;
+	}
+	wm8990_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+	return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	u32 reg_shift = kcontrol->private_value & 0xfff;
+	int ret = 0;
+	u16 reg;
+
+	switch (reg_shift) {
+	case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
+		reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER1);
+		if (reg & WM8990_LDLO) {
+			printk(KERN_WARNING
+			"Cannot set as Output Mixer 1 LDLO Set\n");
+			ret = -1;
+		}
+		break;
+	case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
+		reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER2);
+		if (reg & WM8990_RDRO) {
+			printk(KERN_WARNING
+			"Cannot set as Output Mixer 2 RDRO Set\n");
+			ret = -1;
+		}
+		break;
+	case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
+		reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+		if (reg & WM8990_LDSPK) {
+			printk(KERN_WARNING
+			"Cannot set as Speaker Mixer LDSPK Set\n");
+			ret = -1;
+		}
+		break;
+	case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
+		reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+		if (reg & WM8990_RDSPK) {
+			printk(KERN_WARNING
+			"Cannot set as Speaker Mixer RDSPK Set\n");
+			ret = -1;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN1 Switch", WM8990_INPUT_MIXER2, WM8990_LMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN2 Switch", WM8990_INPUT_MIXER2, WM8990_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_lin34_pga_controls[] = {
+SOC_DAPM_SINGLE("LIN3 Switch", WM8990_INPUT_MIXER2, WM8990_LMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("LIN4 Switch", WM8990_INPUT_MIXER2, WM8990_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8990_dapm_rin12_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN1 Switch", WM8990_INPUT_MIXER2, WM8990_RMN1_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN2 Switch", WM8990_INPUT_MIXER2, WM8990_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8990_dapm_rin34_pga_controls[] = {
+SOC_DAPM_SINGLE("RIN3 Switch", WM8990_INPUT_MIXER2, WM8990_RMN3_BIT, 1, 0),
+SOC_DAPM_SINGLE("RIN4 Switch", WM8990_INPUT_MIXER2, WM8990_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8990_dapm_inmixl_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8990_INPUT_MIXER3,
+	WM8990_LDBVOL_SHIFT, WM8990_LDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8990_INPUT_MIXER5, WM8990_LI2BVOL_SHIFT,
+	7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("LINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+	1, 0),
+SOC_DAPM_SINGLE("LINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+	1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8990_dapm_inmixr_controls[] = {
+SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8990_INPUT_MIXER4,
+	WM8990_RDBVOL_SHIFT, WM8990_RDBVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8990_INPUT_MIXER6, WM8990_RI2BVOL_SHIFT,
+	7, 0, in_mix_tlv),
+SOC_DAPM_SINGLE("RINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT,
+	1, 0),
+SOC_DAPM_SINGLE("RINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT,
+	1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8990_ainlmux[] =
+	{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8990_ainlmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT,
+	ARRAY_SIZE(wm8990_ainlmux), wm8990_ainlmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainlmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8990_ainrmux[] =
+	{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8990_ainrmux_enum =
+SOC_ENUM_SINGLE(WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT,
+	ARRAY_SIZE(wm8990_ainrmux), wm8990_ainrmux);
+
+static const struct snd_kcontrol_new wm8990_dapm_ainrmux_controls =
+SOC_DAPM_ENUM("Route", wm8990_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8990_dapm_rxvoice_controls[] = {
+SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8990_INPUT_MIXER5, WM8990_LR4BVOL_SHIFT,
+			WM8990_LR4BVOL_MASK, 0, in_mix_tlv),
+SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8990_INPUT_MIXER6, WM8990_RL4BVOL_SHIFT,
+			WM8990_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lomix_controls[] = {
+SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LRBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LLBLO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LRI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LLI3LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LR12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LL12LO_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8990_OUTPUT_MIXER1,
+	WM8990_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8990_dapm_romix_controls[] = {
+SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RLBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RRBRO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RLI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RRI3RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RL12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RR12RO_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8990_OUTPUT_MIXER2,
+	WM8990_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lonmix_controls[] = {
+SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+	WM8990_LLOPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER1,
+	WM8990_LROPGALON_BIT, 1, 0),
+SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8990_LINE_MIXER1,
+	WM8990_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_lopmix_controls[] = {
+SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER1,
+	WM8990_LR12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER1,
+	WM8990_LL12LOP_BIT, 1, 0),
+SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1,
+	WM8990_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ronmix_controls[] = {
+SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+	WM8990_RROPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER2,
+	WM8990_RLOPGARON_BIT, 1, 0),
+SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8990_LINE_MIXER2,
+	WM8990_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8990_dapm_ropmix_controls[] = {
+SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER2,
+	WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER2,
+	WM8990_RR12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2,
+	WM8990_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out3mix_controls[] = {
+SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+	WM8990_LI4O3_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8990_OUT3_4_MIXER,
+	WM8990_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8990_dapm_out4mix_controls[] = {
+SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8990_OUT3_4_MIXER,
+	WM8990_RPGAO4_BIT, 1, 0),
+SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER,
+	WM8990_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8990_dapm_spkmix_controls[] = {
+SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LI2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LB2SPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LOPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8990_SPEAKER_MIXER,
+	WM8990_LDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8990_SPEAKER_MIXER,
+	WM8990_RDSPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8990_SPEAKER_MIXER,
+	WM8990_ROPGASPK_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_RL12ROP_BIT, 1, 0),
+SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8990_SPEAKER_MIXER,
+	WM8990_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8990_dapm_widgets[] = {
+/* Input Side */
+/* Input Lines */
+SND_SOC_DAPM_INPUT("LIN1"),
+SND_SOC_DAPM_INPUT("LIN2"),
+SND_SOC_DAPM_INPUT("LIN3"),
+SND_SOC_DAPM_INPUT("LIN4/RXN"),
+SND_SOC_DAPM_INPUT("RIN3"),
+SND_SOC_DAPM_INPUT("RIN4/RXP"),
+SND_SOC_DAPM_INPUT("RIN1"),
+SND_SOC_DAPM_INPUT("RIN2"),
+SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+/* DACs */
+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2,
+	WM8990_ADCL_ENA_BIT, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8990_POWER_MANAGEMENT_2,
+	WM8990_ADCR_ENA_BIT, 0),
+
+/* Input PGAs */
+SND_SOC_DAPM_MIXER("LIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN12_ENA_BIT,
+	0, &wm8990_dapm_lin12_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lin12_pga_controls)),
+SND_SOC_DAPM_MIXER("LIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN34_ENA_BIT,
+	0, &wm8990_dapm_lin34_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lin34_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN12_ENA_BIT,
+	0, &wm8990_dapm_rin12_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_rin12_pga_controls)),
+SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT,
+	0, &wm8990_dapm_rin34_pga_controls[0],
+	ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)),
+
+/* INMIXL */
+SND_SOC_DAPM_MIXER_E("INMIXL", WM8990_INTDRIVBITS, WM8990_INMIXL_PWR_BIT, 0,
+	&wm8990_dapm_inmixl_controls[0],
+	ARRAY_SIZE(wm8990_dapm_inmixl_controls),
+	inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINLMUX */
+SND_SOC_DAPM_MUX_E("AILNMUX", WM8990_INTDRIVBITS, WM8990_AINLMUX_PWR_BIT, 0,
+	&wm8990_dapm_ainlmux_controls, inmixer_event,
+	SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* INMIXR */
+SND_SOC_DAPM_MIXER_E("INMIXR", WM8990_INTDRIVBITS, WM8990_INMIXR_PWR_BIT, 0,
+	&wm8990_dapm_inmixr_controls[0],
+	ARRAY_SIZE(wm8990_dapm_inmixr_controls),
+	inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* AINRMUX */
+SND_SOC_DAPM_MUX_E("AIRNMUX", WM8990_INTDRIVBITS, WM8990_AINRMUX_PWR_BIT, 0,
+	&wm8990_dapm_ainrmux_controls, inmixer_event,
+	SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+/* Output Side */
+/* DACs */
+SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8990_POWER_MANAGEMENT_3,
+	WM8990_DACL_ENA_BIT, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8990_POWER_MANAGEMENT_3,
+	WM8990_DACR_ENA_BIT, 0),
+
+/* LOMIX */
+SND_SOC_DAPM_MIXER_E("LOMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOMIX_ENA_BIT,
+	0, &wm8990_dapm_lomix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lomix_controls),
+	outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LONMIX */
+SND_SOC_DAPM_MIXER("LONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LON_ENA_BIT, 0,
+	&wm8990_dapm_lonmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lonmix_controls)),
+
+/* LOPMIX */
+SND_SOC_DAPM_MIXER("LOPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOP_ENA_BIT, 0,
+	&wm8990_dapm_lopmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_lopmix_controls)),
+
+/* OUT3MIX */
+SND_SOC_DAPM_MIXER("OUT3MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT3_ENA_BIT, 0,
+	&wm8990_dapm_out3mix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_out3mix_controls)),
+
+/* SPKMIX */
+SND_SOC_DAPM_MIXER_E("SPKMIX", WM8990_POWER_MANAGEMENT_1, WM8990_SPK_ENA_BIT, 0,
+	&wm8990_dapm_spkmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_spkmix_controls), outmixer_event,
+	SND_SOC_DAPM_PRE_REG),
+
+/* OUT4MIX */
+SND_SOC_DAPM_MIXER("OUT4MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT4_ENA_BIT, 0,
+	&wm8990_dapm_out4mix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_out4mix_controls)),
+
+/* ROPMIX */
+SND_SOC_DAPM_MIXER("ROPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROP_ENA_BIT, 0,
+	&wm8990_dapm_ropmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_ropmix_controls)),
+
+/* RONMIX */
+SND_SOC_DAPM_MIXER("RONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_RON_ENA_BIT, 0,
+	&wm8990_dapm_ronmix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_ronmix_controls)),
+
+/* ROMIX */
+SND_SOC_DAPM_MIXER_E("ROMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROMIX_ENA_BIT,
+	0, &wm8990_dapm_romix_controls[0],
+	ARRAY_SIZE(wm8990_dapm_romix_controls),
+	outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+/* LOUT PGA */
+SND_SOC_DAPM_PGA("LOUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_LOUT_ENA_BIT, 0,
+	NULL, 0),
+
+/* ROUT PGA */
+SND_SOC_DAPM_PGA("ROUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_ROUT_ENA_BIT, 0,
+	NULL, 0),
+
+/* LOPGA */
+SND_SOC_DAPM_PGA("LOPGA", WM8990_POWER_MANAGEMENT_3, WM8990_LOPGA_ENA_BIT, 0,
+	NULL, 0),
+
+/* ROPGA */
+SND_SOC_DAPM_PGA("ROPGA", WM8990_POWER_MANAGEMENT_3, WM8990_ROPGA_ENA_BIT, 0,
+	NULL, 0),
+
+/* MICBIAS */
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8990_POWER_MANAGEMENT_1,
+	WM8990_MICBIAS_ENA_BIT, 0),
+
+SND_SOC_DAPM_OUTPUT("LON"),
+SND_SOC_DAPM_OUTPUT("LOP"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("OUT4"),
+SND_SOC_DAPM_OUTPUT("ROP"),
+SND_SOC_DAPM_OUTPUT("RON"),
+
+SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Make DACs turn on when playing even if not mixed into any outputs */
+	{"Internal DAC Sink", NULL, "Left DAC"},
+	{"Internal DAC Sink", NULL, "Right DAC"},
+
+	/* Make ADCs turn on when recording even if not mixed from any inputs */
+	{"Left ADC", NULL, "Internal ADC Source"},
+	{"Right ADC", NULL, "Internal ADC Source"},
+
+	/* Input Side */
+	/* LIN12 PGA */
+	{"LIN12 PGA", "LIN1 Switch", "LIN1"},
+	{"LIN12 PGA", "LIN2 Switch", "LIN2"},
+	/* LIN34 PGA */
+	{"LIN34 PGA", "LIN3 Switch", "LIN3"},
+	{"LIN34 PGA", "LIN4 Switch", "LIN4"},
+	/* INMIXL */
+	{"INMIXL", "Record Left Volume", "LOMIX"},
+	{"INMIXL", "LIN2 Volume", "LIN2"},
+	{"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+	{"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+	/* AILNMUX */
+	{"AILNMUX", "INMIXL Mix", "INMIXL"},
+	{"AILNMUX", "DIFFINL Mix", "LIN12PGA"},
+	{"AILNMUX", "DIFFINL Mix", "LIN34PGA"},
+	{"AILNMUX", "RXVOICE Mix", "LIN4/RXN"},
+	{"AILNMUX", "RXVOICE Mix", "RIN4/RXP"},
+	/* ADC */
+	{"Left ADC", NULL, "AILNMUX"},
+
+	/* RIN12 PGA */
+	{"RIN12 PGA", "RIN1 Switch", "RIN1"},
+	{"RIN12 PGA", "RIN2 Switch", "RIN2"},
+	/* RIN34 PGA */
+	{"RIN34 PGA", "RIN3 Switch", "RIN3"},
+	{"RIN34 PGA", "RIN4 Switch", "RIN4"},
+	/* INMIXL */
+	{"INMIXR", "Record Right Volume", "ROMIX"},
+	{"INMIXR", "RIN2 Volume", "RIN2"},
+	{"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+	{"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+	/* AIRNMUX */
+	{"AIRNMUX", "INMIXR Mix", "INMIXR"},
+	{"AIRNMUX", "DIFFINR Mix", "RIN12PGA"},
+	{"AIRNMUX", "DIFFINR Mix", "RIN34PGA"},
+	{"AIRNMUX", "RXVOICE Mix", "RIN4/RXN"},
+	{"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"},
+	/* ADC */
+	{"Right ADC", NULL, "AIRNMUX"},
+
+	/* LOMIX */
+	{"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+	{"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+	{"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+	/* ROMIX */
+	{"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+	{"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+	{"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+	/* SPKMIX */
+	{"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+	{"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+	{"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+	{"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+	{"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+	{"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+	{"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+	{"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+	/* LONMIX */
+	{"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+	/* LOPMIX */
+	{"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+	/* OUT3MIX */
+	{"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXP"},
+	{"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+	/* OUT4MIX */
+	{"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+	{"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
+
+	/* RONMIX */
+	{"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+	/* ROPMIX */
+	{"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+	/* Out Mixer PGAs */
+	{"LOPGA", NULL, "LOMIX"},
+	{"ROPGA", NULL, "ROMIX"},
+
+	{"LOUT PGA", NULL, "LOMIX"},
+	{"ROUT PGA", NULL, "ROMIX"},
+
+	/* Output Pins */
+	{"LON", NULL, "LONMIX"},
+	{"LOP", NULL, "LOPMIX"},
+	{"OUT", NULL, "OUT3MIX"},
+	{"LOUT", NULL, "LOUT PGA"},
+	{"SPKN", NULL, "SPKMIX"},
+	{"ROUT", NULL, "ROUT PGA"},
+	{"OUT4", NULL, "OUT4MIX"},
+	{"ROP", NULL, "ROPMIX"},
+	{"RON", NULL, "RONMIX"},
+};
+
+static int wm8990_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8990_dapm_widgets,
+				  ARRAY_SIZE(wm8990_dapm_widgets));
+
+	/* set up the WM8990 audio map */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+	u32 div2;
+	u32 n;
+	u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+	unsigned int source)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div->div2 = 1;
+		Ndiv = target / source;
+	} else
+		pll_div->div2 = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+		"WM8990 N value outwith recommended range! N = %d\n", Ndiv);
+
+	pll_div->n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div->k = K;
+}
+
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	u16 reg;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct _pll_div pll_div;
+
+	if (freq_in && freq_out) {
+		pll_factors(&pll_div, freq_out * 4, freq_in);
+
+		/* Turn on PLL */
+		reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+		reg |= WM8990_PLL_ENA;
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+
+		/* sysclk comes from PLL */
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2);
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
+
+		/* set up N , fractional mode and pre-divisor if neccessary */
+		wm8990_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+			(pll_div.div2?WM8990_PRESCALE:0));
+		wm8990_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
+		wm8990_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
+	} else {
+		/* Turn on PLL */
+		reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+		reg &= ~WM8990_PLL_ENA;
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+	}
+	return 0;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int wm8990_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8990_priv *wm8990 = codec->private_data;
+
+	wm8990->sysclk = freq;
+	return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 audio1, audio3;
+
+	audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+	audio3 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_3);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		audio3 &= ~WM8990_AIF_MSTR1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		audio3 |= WM8990_AIF_MSTR1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	audio1 &= ~WM8990_AIF_FMT_MASK;
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		audio1 |= WM8990_AIF_TMF_I2S;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		audio1 |= WM8990_AIF_TMF_RIGHTJ;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		audio1 |= WM8990_AIF_TMF_LEFTJ;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		audio1 |= WM8990_AIF_TMF_DSP;
+		audio1 &= ~WM8990_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		audio1 |= WM8990_AIF_TMF_DSP | WM8990_AIF_LRCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+	return 0;
+}
+
+static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+		int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	switch (div_id) {
+	case WM8990_MCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+			~WM8990_MCLK_DIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		break;
+	case WM8990_DACCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+			~WM8990_DAC_CLKDIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		break;
+	case WM8990_ADCCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+			~WM8990_ADC_CLKDIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		break;
+	case WM8990_BCLK_DIV:
+		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_1) &
+			~WM8990_BCLK_DIV_MASK;
+		wm8990_write(codec, WM8990_CLOCKING_1, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8990_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+
+	audio1 &= ~WM8990_AIF_WL_MASK;
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		audio1 |= WM8990_AIF_WL_20BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		audio1 |= WM8990_AIF_WL_24BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		audio1 |= WM8990_AIF_WL_32BITS;
+		break;
+	}
+
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+	return 0;
+}
+
+static int wm8990_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 val;
+
+	val  = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+
+	if (mute)
+		wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+	else
+		wm8990_write(codec, WM8990_DAC_CTRL, val);
+
+	return 0;
+}
+
+static int wm8990_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 val;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Enable all output discharge bits */
+			wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+				WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+				WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+				WM8990_DIS_ROUT);
+
+			/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+				     WM8990_BUFDCOPEN | WM8990_POBCTRL |
+				     WM8990_VMIDTOG);
+
+			/* Delay to allow output caps to discharge */
+			msleep(msecs_to_jiffies(300));
+
+			/* Disable VMIDTOG */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+				     WM8990_BUFDCOPEN | WM8990_POBCTRL);
+
+			/* disable all output discharge bits */
+			wm8990_write(codec, WM8990_ANTIPOP1, 0);
+
+			/* Enable outputs */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+
+			msleep(msecs_to_jiffies(50));
+
+			/* Enable VMID at 2x50k */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+
+			msleep(msecs_to_jiffies(100));
+
+			/* Enable VREF */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+			msleep(msecs_to_jiffies(600));
+
+			/* Enable BUFIOEN */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+				     WM8990_BUFDCOPEN | WM8990_POBCTRL |
+				     WM8990_BUFIOEN);
+
+			/* Disable outputs */
+			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+
+			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+		} else {
+			/* ON -> standby */
+
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Enable POBCTRL and SOFT_ST */
+		wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+			WM8990_POBCTRL | WM8990_BUFIOEN);
+
+		/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+		wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+			WM8990_BUFDCOPEN | WM8990_POBCTRL |
+			WM8990_BUFIOEN);
+
+		/* mute DAC */
+		val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL);
+		wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+
+		/* Enable any disabled outputs */
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+
+		/* Disable VMID */
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+
+		msleep(msecs_to_jiffies(300));
+
+		/* Enable all output discharge bits */
+		wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+			WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
+			WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
+			WM8990_DIS_ROUT);
+
+		/* Disable VREF */
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+
+		/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+		wm8990_write(codec, WM8990_ANTIPOP2, 0x0);
+		break;
+	}
+
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8990_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+	SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
+	SNDRV_PCM_RATE_48000)
+
+#define WM8990_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * The WM8990 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+struct snd_soc_dai wm8990_dai = {
+/* ADC/DAC on primary */
+	.name = "WM8990 ADC/DAC Primary",
+	.id = 1,
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8990_RATES,
+		.formats = WM8990_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8990_RATES,
+		.formats = WM8990_FORMATS,},
+	.ops = {
+		.hw_params = wm8990_hw_params,},
+	.dai_ops = {
+		.digital_mute = wm8990_mute,
+		.set_fmt = wm8990_set_dai_fmt,
+		.set_clkdiv = wm8990_set_dai_clkdiv,
+		.set_pll = wm8990_set_dai_pll,
+		.set_sysclk = wm8990_set_dai_sysclk,
+	},
+};
+EXPORT_SYMBOL_GPL(wm8990_dai);
+
+static int wm8990_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	/* we only need to suspend if we are a valid card */
+	if (!codec->card)
+		return 0;
+
+	wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8990_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* we only need to resume if we are a valid card */
+	if (!codec->card)
+		return 0;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
+		if (i + 1 == WM8990_RESET)
+			continue;
+		data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+
+	wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	return 0;
+}
+
+/*
+ * initialise the WM8990 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8990_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 reg;
+	int ret = 0;
+
+	codec->name = "WM8990";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8990_read_reg_cache;
+	codec->write = wm8990_write;
+	codec->set_bias_level = wm8990_set_bias_level;
+	codec->dai = &wm8990_dai;
+	codec->num_dai = 2;
+	codec->reg_cache_size = ARRAY_SIZE(wm8990_reg);
+	codec->reg_cache = kmemdup(wm8990_reg, sizeof(wm8990_reg), GFP_KERNEL);
+
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	wm8990_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8990: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* charge output caps */
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	reg = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_4);
+	wm8990_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
+
+	reg = wm8990_read_reg_cache(codec, WM8990_GPIO1_GPIO2) &
+		~WM8990_GPIO1_SEL_MASK;
+	wm8990_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
+
+	reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+	wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
+
+	wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+	wm8990_add_controls(codec);
+	wm8990_add_widgets(codec);
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8990: failed to register card\n");
+		goto card_err;
+	}
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+/* If the i2c layer weren't so broken, we could pass this kind of data
+   around */
+static struct snd_soc_device *wm8990_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM891 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    low  = 0x34
+ *    high = 0x36
+ */
+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8990_i2c_driver;
+static struct i2c_client client_template;
+
+static int wm8990_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	struct snd_soc_device *socdev = wm8990_socdev;
+	struct wm8990_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct i2c_client *i2c;
+	int ret;
+
+	if (addr != setup->i2c_address)
+		return -ENODEV;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		pr_err("failed to attach codec at addr %x\n", addr);
+		goto err;
+	}
+
+	ret = wm8990_init(socdev);
+	if (ret < 0) {
+		pr_err("failed to initialise WM8990\n");
+		goto err;
+	}
+	return ret;
+
+err:
+	kfree(codec);
+	kfree(i2c);
+	return ret;
+}
+
+static int wm8990_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	i2c_detach_client(client);
+	kfree(codec->reg_cache);
+	kfree(client);
+	return 0;
+}
+
+static int wm8990_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8990_codec_probe);
+}
+
+static struct i2c_driver wm8990_i2c_driver = {
+	.driver = {
+		.name = "WM8990 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.attach_adapter = wm8990_i2c_attach,
+	.detach_client =  wm8990_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8990",
+	.driver = &wm8990_i2c_driver,
+};
+#endif
+
+static int wm8990_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8990_setup_data *setup;
+	struct snd_soc_codec *codec;
+	struct wm8990_priv *wm8990;
+	int ret = 0;
+
+	pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION);
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	wm8990 = kzalloc(sizeof(struct wm8990_priv), GFP_KERNEL);
+	if (wm8990 == NULL) {
+		kfree(codec);
+		return -ENOMEM;
+	}
+
+	codec->private_data = wm8990;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	wm8990_socdev = socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		normal_i2c[0] = setup->i2c_address;
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = i2c_add_driver(&wm8990_i2c_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add i2c driver");
+	}
+#else
+		/* Add other interfaces here */
+#endif
+	return ret;
+}
+
+/* power down chip */
+static int wm8990_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8990_i2c_driver);
+#endif
+	kfree(codec->private_data);
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8990 = {
+	.probe =	wm8990_probe,
+	.remove =	wm8990_remove,
+	.suspend =	wm8990_suspend,
+	.resume =	wm8990_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
+
+MODULE_DESCRIPTION("ASoC WM8990 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
new file mode 100644
index 0000000..6bea574
--- /dev/null
+++ b/sound/soc/codecs/wm8990.h
@@ -0,0 +1,832 @@
+/*
+ * wm8990.h  --  audio driver for WM8990
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __WM8990REGISTERDEFS_H__
+#define __WM8990REGISTERDEFS_H__
+
+/*
+ * Register values.
+ */
+#define WM8990_RESET                            0x00
+#define WM8990_POWER_MANAGEMENT_1               0x01
+#define WM8990_POWER_MANAGEMENT_2               0x02
+#define WM8990_POWER_MANAGEMENT_3               0x03
+#define WM8990_AUDIO_INTERFACE_1                0x04
+#define WM8990_AUDIO_INTERFACE_2                0x05
+#define WM8990_CLOCKING_1                       0x06
+#define WM8990_CLOCKING_2                       0x07
+#define WM8990_AUDIO_INTERFACE_3                0x08
+#define WM8990_AUDIO_INTERFACE_4                0x09
+#define WM8990_DAC_CTRL                         0x0A
+#define WM8990_LEFT_DAC_DIGITAL_VOLUME          0x0B
+#define WM8990_RIGHT_DAC_DIGITAL_VOLUME         0x0C
+#define WM8990_DIGITAL_SIDE_TONE                0x0D
+#define WM8990_ADC_CTRL                         0x0E
+#define WM8990_LEFT_ADC_DIGITAL_VOLUME          0x0F
+#define WM8990_RIGHT_ADC_DIGITAL_VOLUME         0x10
+#define WM8990_GPIO_CTRL_1                      0x12
+#define WM8990_GPIO1_GPIO2                      0x13
+#define WM8990_GPIO3_GPIO4                      0x14
+#define WM8990_GPIO5_GPIO6                      0x15
+#define WM8990_GPIOCTRL_2                       0x16
+#define WM8990_GPIO_POL                         0x17
+#define WM8990_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8990_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8990_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8990_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8990_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8990_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8990_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8990_OUT3_4_VOLUME                    0x1F
+#define WM8990_LEFT_OPGA_VOLUME                 0x20
+#define WM8990_RIGHT_OPGA_VOLUME                0x21
+#define WM8990_SPEAKER_VOLUME                   0x22
+#define WM8990_CLASSD1                          0x23
+#define WM8990_CLASSD3                          0x25
+#define WM8990_INPUT_MIXER1                     0x27
+#define WM8990_INPUT_MIXER2                     0x28
+#define WM8990_INPUT_MIXER3                     0x29
+#define WM8990_INPUT_MIXER4                     0x2A
+#define WM8990_INPUT_MIXER5                     0x2B
+#define WM8990_INPUT_MIXER6                     0x2C
+#define WM8990_OUTPUT_MIXER1                    0x2D
+#define WM8990_OUTPUT_MIXER2                    0x2E
+#define WM8990_OUTPUT_MIXER3                    0x2F
+#define WM8990_OUTPUT_MIXER4                    0x30
+#define WM8990_OUTPUT_MIXER5                    0x31
+#define WM8990_OUTPUT_MIXER6                    0x32
+#define WM8990_OUT3_4_MIXER                     0x33
+#define WM8990_LINE_MIXER1                      0x34
+#define WM8990_LINE_MIXER2                      0x35
+#define WM8990_SPEAKER_MIXER                    0x36
+#define WM8990_ADDITIONAL_CONTROL               0x37
+#define WM8990_ANTIPOP1                         0x38
+#define WM8990_ANTIPOP2                         0x39
+#define WM8990_MICBIAS                          0x3A
+#define WM8990_PLL1                             0x3C
+#define WM8990_PLL2                             0x3D
+#define WM8990_PLL3                             0x3E
+#define WM8990_INTDRIVBITS			0x3F
+
+#define WM8990_REGISTER_COUNT                   60
+#define WM8990_MAX_REGISTER                     0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8990_SW_RESET_CHIP_ID_MASK            0xFFFF  /* SW_RESET_CHIP_ID */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8990_SPK_ENA                          0x1000  /* SPK_ENA */
+#define WM8990_SPK_ENA_BIT			12
+#define WM8990_OUT3_ENA                         0x0800  /* OUT3_ENA */
+#define WM8990_OUT3_ENA_BIT			11
+#define WM8990_OUT4_ENA                         0x0400  /* OUT4_ENA */
+#define WM8990_OUT4_ENA_BIT			10
+#define WM8990_LOUT_ENA                         0x0200  /* LOUT_ENA */
+#define WM8990_LOUT_ENA_BIT			9
+#define WM8990_ROUT_ENA                         0x0100  /* ROUT_ENA */
+#define WM8990_ROUT_ENA_BIT			8
+#define WM8990_MICBIAS_ENA                      0x0010  /* MICBIAS_ENA */
+#define WM8990_MICBIAS_ENA_BIT			4
+#define WM8990_VMID_MODE_MASK                   0x0006  /* VMID_MODE - [2:1] */
+#define WM8990_VREF_ENA                         0x0001  /* VREF_ENA */
+#define WM8990_VREF_ENA_BIT			0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8990_PLL_ENA                          0x8000  /* PLL_ENA */
+#define WM8990_PLL_ENA_BIT			15
+#define WM8990_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8990_TSHUT_ENA_BIT			14
+#define WM8990_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8990_TSHUT_OPDIS_BIT			13
+#define WM8990_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8990_OPCLK_ENA_BIT			11
+#define WM8990_AINL_ENA                         0x0200  /* AINL_ENA */
+#define WM8990_AINL_ENA_BIT			9
+#define WM8990_AINR_ENA                         0x0100  /* AINR_ENA */
+#define WM8990_AINR_ENA_BIT			8
+#define WM8990_LIN34_ENA                        0x0080  /* LIN34_ENA */
+#define WM8990_LIN34_ENA_BIT			7
+#define WM8990_LIN12_ENA                        0x0040  /* LIN12_ENA */
+#define WM8990_LIN12_ENA_BIT			6
+#define WM8990_RIN34_ENA                        0x0020  /* RIN34_ENA */
+#define WM8990_RIN34_ENA_BIT			5
+#define WM8990_RIN12_ENA                        0x0010  /* RIN12_ENA */
+#define WM8990_RIN12_ENA_BIT			4
+#define WM8990_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8990_ADCL_ENA_BIT			1
+#define WM8990_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8990_ADCR_ENA_BIT			0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8990_LON_ENA                          0x2000  /* LON_ENA */
+#define WM8990_LON_ENA_BIT			13
+#define WM8990_LOP_ENA                          0x1000  /* LOP_ENA */
+#define WM8990_LOP_ENA_BIT			12
+#define WM8990_RON_ENA                          0x0800  /* RON_ENA */
+#define WM8990_RON_ENA_BIT			11
+#define WM8990_ROP_ENA                          0x0400  /* ROP_ENA */
+#define WM8990_ROP_ENA_BIT			10
+#define WM8990_LOPGA_ENA                        0x0080  /* LOPGA_ENA */
+#define WM8990_LOPGA_ENA_BIT			7
+#define WM8990_ROPGA_ENA                        0x0040  /* ROPGA_ENA */
+#define WM8990_ROPGA_ENA_BIT			6
+#define WM8990_LOMIX_ENA                        0x0020  /* LOMIX_ENA */
+#define WM8990_LOMIX_ENA_BIT			5
+#define WM8990_ROMIX_ENA                        0x0010  /* ROMIX_ENA */
+#define WM8990_ROMIX_ENA_BIT			4
+#define WM8990_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8990_DACL_ENA_BIT			1
+#define WM8990_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8990_DACR_ENA_BIT			0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8990_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8990_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8990_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8990_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8990_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8990_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8990_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8990_AIF_WL_16BITS			(0 << 5)
+#define WM8990_AIF_WL_20BITS			(1 << 5)
+#define WM8990_AIF_WL_24BITS			(2 << 5)
+#define WM8990_AIF_WL_32BITS			(3 << 5)
+#define WM8990_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8990_AIF_TMF_RIGHTJ			(0 << 3)
+#define WM8990_AIF_TMF_LEFTJ			(1 << 3)
+#define WM8990_AIF_TMF_I2S			(2 << 3)
+#define WM8990_AIF_TMF_DSP			(3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8990_DACL_SRC                         0x8000  /* DACL_SRC */
+#define WM8990_DACR_SRC                         0x4000  /* DACR_SRC */
+#define WM8990_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8990_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8990_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST */
+#define WM8990_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8990_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8990_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8990_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8990_LOOPBACK                         0x0001  /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8990_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8990_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8990_OPCLKDIV_MASK                    0x1E00  /* OPCLKDIV - [12:9] */
+#define WM8990_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8990_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8990_BCLK_DIV_1			(0x0 << 1)
+#define WM8990_BCLK_DIV_1_5			(0x1 << 1)
+#define WM8990_BCLK_DIV_2			(0x2 << 1)
+#define WM8990_BCLK_DIV_3			(0x3 << 1)
+#define WM8990_BCLK_DIV_4			(0x4 << 1)
+#define WM8990_BCLK_DIV_5_5			(0x5 << 1)
+#define WM8990_BCLK_DIV_6			(0x6 << 1)
+#define WM8990_BCLK_DIV_8			(0x7 << 1)
+#define WM8990_BCLK_DIV_11			(0x8 << 1)
+#define WM8990_BCLK_DIV_12			(0x9 << 1)
+#define WM8990_BCLK_DIV_16			(0xA << 1)
+#define WM8990_BCLK_DIV_22			(0xB << 1)
+#define WM8990_BCLK_DIV_24			(0xC << 1)
+#define WM8990_BCLK_DIV_32			(0xD << 1)
+#define WM8990_BCLK_DIV_44			(0xE << 1)
+#define WM8990_BCLK_DIV_48			(0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8990_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8990_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8990_CLK_FORCE                        0x2000  /* CLK_FORCE */
+#define WM8990_MCLK_DIV_MASK                    0x1800  /* MCLK_DIV - [12:11] */
+#define WM8990_MCLK_DIV_1			(0 << 11)
+#define WM8990_MCLK_DIV_2			(2 << 11)
+#define WM8990_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8990_ADC_CLKDIV_MASK                  0x00E0  /* ADC_CLKDIV */
+#define WM8990_ADC_CLKDIV_1			(0 << 5)
+#define WM8990_ADC_CLKDIV_1_5			(1 << 5)
+#define WM8990_ADC_CLKDIV_2			(2 << 5)
+#define WM8990_ADC_CLKDIV_3			(3 << 5)
+#define WM8990_ADC_CLKDIV_4			(4 << 5)
+#define WM8990_ADC_CLKDIV_5_5			(5 << 5)
+#define WM8990_ADC_CLKDIV_6			(6 << 5)
+#define WM8990_DAC_CLKDIV_MASK                  0x001C  /* DAC_CLKDIV - [4:2] */
+#define WM8990_DAC_CLKDIV_1			(0 << 2)
+#define WM8990_DAC_CLKDIV_1_5			(1 << 2)
+#define WM8990_DAC_CLKDIV_2			(2 << 2)
+#define WM8990_DAC_CLKDIV_3			(3 << 2)
+#define WM8990_DAC_CLKDIV_4			(4 << 2)
+#define WM8990_DAC_CLKDIV_5_5			(5 << 2)
+#define WM8990_DAC_CLKDIV_6			(6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8990_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8990_AIF_MSTR2                        0x4000  /* AIF_MSTR2 */
+#define WM8990_AIF_SEL                          0x2000  /* AIF_SEL */
+#define WM8990_ADCLRC_DIR                       0x0800  /* ADCLRC_DIR */
+#define WM8990_ADCLRC_RATE_MASK                 0x07FF  /* ADCLRC_RATE */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8990_ALRCGPIO1                        0x8000  /* ALRCGPIO1 */
+#define WM8990_ALRCBGPIO6                       0x4000  /* ALRCBGPIO6 */
+#define WM8990_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8990_DACLRC_DIR                       0x0800  /* DACLRC_DIR */
+#define WM8990_DACLRC_RATE_MASK                 0x07FF  /* DACLRC_RATE */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8990_AIF_LRCLKRATE                    0x0400  /* AIF_LRCLKRATE */
+#define WM8990_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8990_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8990_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8990_DAC_MUTEMODE                     0x0040  /* DAC_MUTEMODE */
+#define WM8990_DEEMP_MASK                       0x0030  /* DEEMP - [5:4] */
+#define WM8990_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8990_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8990_DACR_DATINV                      0x0001  /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8990_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8990_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8990_DACL_VOL_SHIFT			0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8990_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8990_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8990_DACR_VOL_SHIFT			0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8990_ADCL_DAC_SVOL_MASK               0x0F  /* ADCL_DAC_SVOL */
+#define WM8990_ADCL_DAC_SVOL_SHIFT		9
+#define WM8990_ADCR_DAC_SVOL_MASK               0x0F  /* ADCR_DAC_SVOL */
+#define WM8990_ADCR_DAC_SVOL_SHIFT		5
+#define WM8990_ADC_TO_DACL_MASK                 0x03  /* ADC_TO_DACL - [3:2] */
+#define WM8990_ADC_TO_DACL_SHIFT		2
+#define WM8990_ADC_TO_DACR_MASK                 0x03  /* ADC_TO_DACR - [1:0] */
+#define WM8990_ADC_TO_DACR_SHIFT		0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8990_ADC_HPF_ENA                      0x0100  /* ADC_HPF_ENA */
+#define WM8990_ADC_HPF_ENA_BIT			8
+#define WM8990_ADC_HPF_CUT_MASK                 0x03  /* ADC_HPF_CUT - [6:5] */
+#define WM8990_ADC_HPF_CUT_SHIFT		5
+#define WM8990_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8990_ADCL_DATINV_BIT			1
+#define WM8990_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8990_ADCR_DATINV_BIT			0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8990_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8990_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8990_ADCL_VOL_SHIFT			0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8990_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8990_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8990_ADCR_VOL_SHIFT			0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8990_IRQ                              0x1000  /* IRQ */
+#define WM8990_TEMPOK                           0x0800  /* TEMPOK */
+#define WM8990_MICSHRT                          0x0400  /* MICSHRT */
+#define WM8990_MICDET                           0x0200  /* MICDET */
+#define WM8990_PLL_LCK                          0x0100  /* PLL_LCK */
+#define WM8990_GPI8_STATUS                      0x0080  /* GPI8_STATUS */
+#define WM8990_GPI7_STATUS                      0x0040  /* GPI7_STATUS */
+#define WM8990_GPIO6_STATUS                     0x0020  /* GPIO6_STATUS */
+#define WM8990_GPIO5_STATUS                     0x0010  /* GPIO5_STATUS */
+#define WM8990_GPIO4_STATUS                     0x0008  /* GPIO4_STATUS */
+#define WM8990_GPIO3_STATUS                     0x0004  /* GPIO3_STATUS */
+#define WM8990_GPIO2_STATUS                     0x0002  /* GPIO2_STATUS */
+#define WM8990_GPIO1_STATUS                     0x0001  /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8990_GPIO2_DEB_ENA                    0x8000  /* GPIO2_DEB_ENA */
+#define WM8990_GPIO2_IRQ_ENA                    0x4000  /* GPIO2_IRQ_ENA */
+#define WM8990_GPIO2_PU                         0x2000  /* GPIO2_PU */
+#define WM8990_GPIO2_PD                         0x1000  /* GPIO2_PD */
+#define WM8990_GPIO2_SEL_MASK                   0x0F00  /* GPIO2_SEL - [11:8] */
+#define WM8990_GPIO1_DEB_ENA                    0x0080  /* GPIO1_DEB_ENA */
+#define WM8990_GPIO1_IRQ_ENA                    0x0040  /* GPIO1_IRQ_ENA */
+#define WM8990_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8990_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8990_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8990_GPIO4_DEB_ENA                    0x8000  /* GPIO4_DEB_ENA */
+#define WM8990_GPIO4_IRQ_ENA                    0x4000  /* GPIO4_IRQ_ENA */
+#define WM8990_GPIO4_PU                         0x2000  /* GPIO4_PU */
+#define WM8990_GPIO4_PD                         0x1000  /* GPIO4_PD */
+#define WM8990_GPIO4_SEL_MASK                   0x0F00  /* GPIO4_SEL - [11:8] */
+#define WM8990_GPIO3_DEB_ENA                    0x0080  /* GPIO3_DEB_ENA */
+#define WM8990_GPIO3_IRQ_ENA                    0x0040  /* GPIO3_IRQ_ENA */
+#define WM8990_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8990_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8990_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8990_GPIO6_DEB_ENA                    0x8000  /* GPIO6_DEB_ENA */
+#define WM8990_GPIO6_IRQ_ENA                    0x4000  /* GPIO6_IRQ_ENA */
+#define WM8990_GPIO6_PU                         0x2000  /* GPIO6_PU */
+#define WM8990_GPIO6_PD                         0x1000  /* GPIO6_PD */
+#define WM8990_GPIO6_SEL_MASK                   0x0F00  /* GPIO6_SEL - [11:8] */
+#define WM8990_GPIO5_DEB_ENA                    0x0080  /* GPIO5_DEB_ENA */
+#define WM8990_GPIO5_IRQ_ENA                    0x0040  /* GPIO5_IRQ_ENA */
+#define WM8990_GPIO5_PU                         0x0020  /* GPIO5_PU */
+#define WM8990_GPIO5_PD                         0x0010  /* GPIO5_PD */
+#define WM8990_GPIO5_SEL_MASK                   0x000F  /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8990_RD_3W_ENA                        0x8000  /* RD_3W_ENA */
+#define WM8990_MODE_3W4W                        0x4000  /* MODE_3W4W */
+#define WM8990_TEMPOK_IRQ_ENA                   0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8990_MICSHRT_IRQ_ENA                  0x0400  /* MICSHRT_IRQ_ENA */
+#define WM8990_MICDET_IRQ_ENA                   0x0200  /* MICDET_IRQ_ENA */
+#define WM8990_PLL_LCK_IRQ_ENA                  0x0100  /* PLL_LCK_IRQ_ENA */
+#define WM8990_GPI8_DEB_ENA                     0x0080  /* GPI8_DEB_ENA */
+#define WM8990_GPI8_IRQ_ENA                     0x0040  /* GPI8_IRQ_ENA */
+#define WM8990_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8990_GPI7_DEB_ENA                     0x0008  /* GPI7_DEB_ENA */
+#define WM8990_GPI7_IRQ_ENA                     0x0004  /* GPI7_IRQ_ENA */
+#define WM8990_GPI7_ENA                         0x0001  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8990_IRQ_INV                          0x1000  /* IRQ_INV */
+#define WM8990_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8990_MICSHRT_POL                      0x0400  /* MICSHRT_POL */
+#define WM8990_MICDET_POL                       0x0200  /* MICDET_POL */
+#define WM8990_PLL_LCK_POL                      0x0100  /* PLL_LCK_POL */
+#define WM8990_GPI8_POL                         0x0080  /* GPI8_POL */
+#define WM8990_GPI7_POL                         0x0040  /* GPI7_POL */
+#define WM8990_GPIO6_POL                        0x0020  /* GPIO6_POL */
+#define WM8990_GPIO5_POL                        0x0010  /* GPIO5_POL */
+#define WM8990_GPIO4_POL                        0x0008  /* GPIO4_POL */
+#define WM8990_GPIO3_POL                        0x0004  /* GPIO3_POL */
+#define WM8990_GPIO2_POL                        0x0002  /* GPIO2_POL */
+#define WM8990_GPIO1_POL                        0x0001  /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_LI12MUTE                         0x0080  /* LI12MUTE */
+#define WM8990_LI12MUTE_BIT			7
+#define WM8990_LI12ZC                           0x0040  /* LI12ZC */
+#define WM8990_LI12ZC_BIT			6
+#define WM8990_LIN12VOL_MASK                    0x001F  /* LIN12VOL - [4:0] */
+#define WM8990_LIN12VOL_SHIFT			0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_LI34MUTE                         0x0080  /* LI34MUTE */
+#define WM8990_LI34MUTE_BIT			7
+#define WM8990_LI34ZC                           0x0040  /* LI34ZC */
+#define WM8990_LI34ZC_BIT			6
+#define WM8990_LIN34VOL_MASK                    0x001F  /* LIN34VOL - [4:0] */
+#define WM8990_LIN34VOL_SHIFT			0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_RI12MUTE                         0x0080  /* RI12MUTE */
+#define WM8990_RI12MUTE_BIT			7
+#define WM8990_RI12ZC                           0x0040  /* RI12ZC */
+#define WM8990_RI12ZC_BIT			6
+#define WM8990_RIN12VOL_MASK                    0x001F  /* RIN12VOL - [4:0] */
+#define WM8990_RIN12VOL_SHIFT			0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8990_IPVU                             0x0100  /* IPVU */
+#define WM8990_RI34MUTE                         0x0080  /* RI34MUTE */
+#define WM8990_RI34MUTE_BIT			7
+#define WM8990_RI34ZC                           0x0040  /* RI34ZC */
+#define WM8990_RI34ZC_BIT			6
+#define WM8990_RIN34VOL_MASK                    0x001F  /* RIN34VOL - [4:0] */
+#define WM8990_RIN34VOL_SHIFT			0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_LOZC                             0x0080  /* LOZC */
+#define WM8990_LOZC_BIT				7
+#define WM8990_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8990_LOUTVOL_SHIFT			0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_ROZC                             0x0080  /* ROZC */
+#define WM8990_ROZC_BIT				7
+#define WM8990_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8990_ROUTVOL_SHIFT			0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8990_LONMUTE                          0x0040  /* LONMUTE */
+#define WM8990_LONMUTE_BIT			6
+#define WM8990_LOPMUTE                          0x0020  /* LOPMUTE */
+#define WM8990_LOPMUTE_BIT			5
+#define WM8990_LOATTN                           0x0010  /* LOATTN */
+#define WM8990_LOATTN_BIT			4
+#define WM8990_RONMUTE                          0x0004  /* RONMUTE */
+#define WM8990_RONMUTE_BIT			2
+#define WM8990_ROPMUTE                          0x0002  /* ROPMUTE */
+#define WM8990_ROPMUTE_BIT			1
+#define WM8990_ROATTN                           0x0001  /* ROATTN */
+#define WM8990_ROATTN_BIT			0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8990_OUT3MUTE                         0x0020  /* OUT3MUTE */
+#define WM8990_OUT3MUTE_BIT			5
+#define WM8990_OUT3ATTN                         0x0010  /* OUT3ATTN */
+#define WM8990_OUT3ATTN_BIT			4
+#define WM8990_OUT4MUTE                         0x0002  /* OUT4MUTE */
+#define WM8990_OUT4MUTE_BIT			1
+#define WM8990_OUT4ATTN                         0x0001  /* OUT4ATTN */
+#define WM8990_OUT4ATTN_BIT			0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_LOPGAZC                          0x0080  /* LOPGAZC */
+#define WM8990_LOPGAZC_BIT			7
+#define WM8990_LOPGAVOL_MASK                    0x007F  /* LOPGAVOL - [6:0] */
+#define WM8990_LOPGAVOL_SHIFT			0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8990_OPVU                             0x0100  /* OPVU */
+#define WM8990_ROPGAZC                          0x0080  /* ROPGAZC */
+#define WM8990_ROPGAZC_BIT			7
+#define WM8990_ROPGAVOL_MASK                    0x007F  /* ROPGAVOL - [6:0] */
+#define WM8990_ROPGAVOL_SHIFT			0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8990_SPKVOL_MASK                      0x0003  /* SPKVOL - [1:0] */
+#define WM8990_SPKVOL_SHIFT			0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8990_CDMODE                           0x0100  /* CDMODE */
+#define WM8990_CDMODE_BIT			8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8990_DCGAIN_MASK                      0x0007  /* DCGAIN - [5:3] */
+#define WM8990_DCGAIN_SHIFT			3
+#define WM8990_ACGAIN_MASK                      0x0007  /* ACGAIN - [2:0] */
+#define WM8990_ACGAIN_SHIFT			0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8990_AINLMODE_MASK                    0x000C  /* AINLMODE - [3:2] */
+#define WM8990_AINLMODE_SHIFT			2
+#define WM8990_AINRMODE_MASK                    0x0003  /* AINRMODE - [1:0] */
+#define WM8990_AINRMODE_SHIFT			0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8990_LMP4				0x0080	/* LMP4 */
+#define WM8990_LMP4_BIT                         7	/* LMP4 */
+#define WM8990_LMN3                             0x0040  /* LMN3 */
+#define WM8990_LMN3_BIT                         6       /* LMN3 */
+#define WM8990_LMP2                             0x0020  /* LMP2 */
+#define WM8990_LMP2_BIT                         5       /* LMP2 */
+#define WM8990_LMN1                             0x0010  /* LMN1 */
+#define WM8990_LMN1_BIT                         4       /* LMN1 */
+#define WM8990_RMP4                             0x0008  /* RMP4 */
+#define WM8990_RMP4_BIT                         3       /* RMP4 */
+#define WM8990_RMN3                             0x0004  /* RMN3 */
+#define WM8990_RMN3_BIT                         2       /* RMN3 */
+#define WM8990_RMP2                             0x0002  /* RMP2 */
+#define WM8990_RMP2_BIT                         1       /* RMP2 */
+#define WM8990_RMN1                             0x0001  /* RMN1 */
+#define WM8990_RMN1_BIT                         0       /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8990_L34MNB                           0x0100  /* L34MNB */
+#define WM8990_L34MNB_BIT			8
+#define WM8990_L34MNBST                         0x0080  /* L34MNBST */
+#define WM8990_L34MNBST_BIT			7
+#define WM8990_L12MNB                           0x0020  /* L12MNB */
+#define WM8990_L12MNB_BIT			5
+#define WM8990_L12MNBST                         0x0010  /* L12MNBST */
+#define WM8990_L12MNBST_BIT			4
+#define WM8990_LDBVOL_MASK                      0x0007  /* LDBVOL - [2:0] */
+#define WM8990_LDBVOL_SHIFT			0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8990_R34MNB                           0x0100  /* R34MNB */
+#define WM8990_R34MNB_BIT			8
+#define WM8990_R34MNBST                         0x0080  /* R34MNBST */
+#define WM8990_R34MNBST_BIT			7
+#define WM8990_R12MNB                           0x0020  /* R12MNB */
+#define WM8990_R12MNB_BIT			5
+#define WM8990_R12MNBST                         0x0010  /* R12MNBST */
+#define WM8990_R12MNBST_BIT			4
+#define WM8990_RDBVOL_MASK                      0x0007  /* RDBVOL - [2:0] */
+#define WM8990_RDBVOL_SHIFT			0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8990_LI2BVOL_MASK                     0x07  /* LI2BVOL - [8:6] */
+#define WM8990_LI2BVOL_SHIFT			6
+#define WM8990_LR4BVOL_MASK                     0x07  /* LR4BVOL - [5:3] */
+#define WM8990_LR4BVOL_SHIFT			3
+#define WM8990_LL4BVOL_MASK                     0x07  /* LL4BVOL - [2:0] */
+#define WM8990_LL4BVOL_SHIFT			0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8990_RI2BVOL_MASK                     0x07  /* RI2BVOL - [8:6] */
+#define WM8990_RI2BVOL_SHIFT			6
+#define WM8990_RL4BVOL_MASK                     0x07  /* RL4BVOL - [5:3] */
+#define WM8990_RL4BVOL_SHIFT			3
+#define WM8990_RR4BVOL_MASK                     0x07  /* RR4BVOL - [2:0] */
+#define WM8990_RR4BVOL_SHIFT			0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8990_LRBLO                            0x0080  /* LRBLO */
+#define WM8990_LRBLO_BIT			7
+#define WM8990_LLBLO                            0x0040  /* LLBLO */
+#define WM8990_LLBLO_BIT			6
+#define WM8990_LRI3LO                           0x0020  /* LRI3LO */
+#define WM8990_LRI3LO_BIT			5
+#define WM8990_LLI3LO                           0x0010  /* LLI3LO */
+#define WM8990_LLI3LO_BIT			4
+#define WM8990_LR12LO                           0x0008  /* LR12LO */
+#define WM8990_LR12LO_BIT			3
+#define WM8990_LL12LO                           0x0004  /* LL12LO */
+#define WM8990_LL12LO_BIT			2
+#define WM8990_LDLO                             0x0001  /* LDLO */
+#define WM8990_LDLO_BIT				0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8990_RLBRO                            0x0080  /* RLBRO */
+#define WM8990_RLBRO_BIT			7
+#define WM8990_RRBRO                            0x0040  /* RRBRO */
+#define WM8990_RRBRO_BIT			6
+#define WM8990_RLI3RO                           0x0020  /* RLI3RO */
+#define WM8990_RLI3RO_BIT			5
+#define WM8990_RRI3RO                           0x0010  /* RRI3RO */
+#define WM8990_RRI3RO_BIT			4
+#define WM8990_RL12RO                           0x0008  /* RL12RO */
+#define WM8990_RL12RO_BIT			3
+#define WM8990_RR12RO                           0x0004  /* RR12RO */
+#define WM8990_RR12RO_BIT			2
+#define WM8990_RDRO                             0x0001  /* RDRO */
+#define WM8990_RDRO_BIT				0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8990_LLI3LOVOL_MASK                   0x07  /* LLI3LOVOL - [8:6] */
+#define WM8990_LLI3LOVOL_SHIFT			6
+#define WM8990_LR12LOVOL_MASK                   0x07  /* LR12LOVOL - [5:3] */
+#define WM8990_LR12LOVOL_SHIFT			3
+#define WM8990_LL12LOVOL_MASK                   0x07  /* LL12LOVOL - [2:0] */
+#define WM8990_LL12LOVOL_SHIFT			0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8990_RRI3ROVOL_MASK                   0x07  /* RRI3ROVOL - [8:6] */
+#define WM8990_RRI3ROVOL_SHIFT			6
+#define WM8990_RL12ROVOL_MASK                   0x07  /* RL12ROVOL - [5:3] */
+#define WM8990_RL12ROVOL_SHIFT			3
+#define WM8990_RR12ROVOL_MASK                   0x07  /* RR12ROVOL - [2:0] */
+#define WM8990_RR12ROVOL_SHIFT			0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8990_LRI3LOVOL_MASK                   0x07  /* LRI3LOVOL - [8:6] */
+#define WM8990_LRI3LOVOL_SHIFT			6
+#define WM8990_LRBLOVOL_MASK                    0x07  /* LRBLOVOL - [5:3] */
+#define WM8990_LRBLOVOL_SHIFT			3
+#define WM8990_LLBLOVOL_MASK                    0x07  /* LLBLOVOL - [2:0] */
+#define WM8990_LLBLOVOL_SHIFT			0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8990_RLI3ROVOL_MASK                   0x07  /* RLI3ROVOL - [8:6] */
+#define WM8990_RLI3ROVOL_SHIFT			6
+#define WM8990_RLBROVOL_MASK                    0x07  /* RLBROVOL - [5:3] */
+#define WM8990_RLBROVOL_SHIFT			3
+#define WM8990_RRBROVOL_MASK                    0x07  /* RRBROVOL - [2:0] */
+#define WM8990_RRBROVOL_SHIFT			0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8990_VSEL_MASK                        0x0180  /* VSEL - [8:7] */
+#define WM8990_LI4O3                            0x0020  /* LI4O3 */
+#define WM8990_LI4O3_BIT			5
+#define WM8990_LPGAO3                           0x0010  /* LPGAO3 */
+#define WM8990_LPGAO3_BIT			4
+#define WM8990_RI4O4                            0x0002  /* RI4O4 */
+#define WM8990_RI4O4_BIT			1
+#define WM8990_RPGAO4                           0x0001  /* RPGAO4 */
+#define WM8990_RPGAO4_BIT			0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8990_LLOPGALON                        0x0040  /* LLOPGALON */
+#define WM8990_LLOPGALON_BIT			6
+#define WM8990_LROPGALON                        0x0020  /* LROPGALON */
+#define WM8990_LROPGALON_BIT			5
+#define WM8990_LOPLON                           0x0010  /* LOPLON */
+#define WM8990_LOPLON_BIT			4
+#define WM8990_LR12LOP                          0x0004  /* LR12LOP */
+#define WM8990_LR12LOP_BIT			2
+#define WM8990_LL12LOP                          0x0002  /* LL12LOP */
+#define WM8990_LL12LOP_BIT			1
+#define WM8990_LLOPGALOP                        0x0001  /* LLOPGALOP */
+#define WM8990_LLOPGALOP_BIT			0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8990_RROPGARON                        0x0040  /* RROPGARON */
+#define WM8990_RROPGARON_BIT			6
+#define WM8990_RLOPGARON                        0x0020  /* RLOPGARON */
+#define WM8990_RLOPGARON_BIT			5
+#define WM8990_ROPRON                           0x0010  /* ROPRON */
+#define WM8990_ROPRON_BIT			4
+#define WM8990_RL12ROP                          0x0004  /* RL12ROP */
+#define WM8990_RL12ROP_BIT			2
+#define WM8990_RR12ROP                          0x0002  /* RR12ROP */
+#define WM8990_RR12ROP_BIT			1
+#define WM8990_RROPGAROP                        0x0001  /* RROPGAROP */
+#define WM8990_RROPGAROP_BIT			0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8990_LB2SPK                           0x0080  /* LB2SPK */
+#define WM8990_LB2SPK_BIT			7
+#define WM8990_RB2SPK                           0x0040  /* RB2SPK */
+#define WM8990_RB2SPK_BIT			6
+#define WM8990_LI2SPK                           0x0020  /* LI2SPK */
+#define WM8990_LI2SPK_BIT			5
+#define WM8990_RI2SPK                           0x0010  /* RI2SPK */
+#define WM8990_RI2SPK_BIT			4
+#define WM8990_LOPGASPK                         0x0008  /* LOPGASPK */
+#define WM8990_LOPGASPK_BIT			3
+#define WM8990_ROPGASPK                         0x0004  /* ROPGASPK */
+#define WM8990_ROPGASPK_BIT			2
+#define WM8990_LDSPK                            0x0002  /* LDSPK */
+#define WM8990_LDSPK_BIT			1
+#define WM8990_RDSPK                            0x0001  /* RDSPK */
+#define WM8990_RDSPK_BIT			0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8990_VROI                             0x0001  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8990_DIS_LLINE                        0x0020  /* DIS_LLINE */
+#define WM8990_DIS_RLINE                        0x0010  /* DIS_RLINE */
+#define WM8990_DIS_OUT3                         0x0008  /* DIS_OUT3 */
+#define WM8990_DIS_OUT4                         0x0004  /* DIS_OUT4 */
+#define WM8990_DIS_LOUT                         0x0002  /* DIS_LOUT */
+#define WM8990_DIS_ROUT                         0x0001  /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8990_SOFTST                           0x0040  /* SOFTST */
+#define WM8990_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8990_BUFDCOPEN                        0x0004  /* BUFDCOPEN */
+#define WM8990_POBCTRL                          0x0002  /* POBCTRL */
+#define WM8990_VMIDTOG                          0x0001  /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8990_MCDSCTH_MASK                     0x00C0  /* MCDSCTH - [7:6] */
+#define WM8990_MCDTHR_MASK                      0x0038  /* MCDTHR - [5:3] */
+#define WM8990_MCD                              0x0004  /* MCD */
+#define WM8990_MBSEL                            0x0001  /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8990_SDM                              0x0080  /* SDM */
+#define WM8990_PRESCALE                         0x0040  /* PRESCALE */
+#define WM8990_PLLN_MASK                        0x000F  /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8990_PLLK1_MASK                       0x00FF  /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8990_PLLK2_MASK                       0x00FF  /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8990_INMIXL_PWR_BIT			0
+#define WM8990_AINLMUX_PWR_BIT			1
+#define WM8990_INMIXR_PWR_BIT			2
+#define WM8990_AINRMUX_PWR_BIT			3
+
+struct wm8990_setup_data {
+	unsigned short i2c_address;
+};
+
+#define WM8990_MCLK_DIV 0
+#define WM8990_DACCLK_DIV 1
+#define WM8990_ADCCLK_DIV 2
+#define WM8990_BCLK_DIV 3
+
+extern struct snd_soc_dai wm8990_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8990;
+
+#endif	/* __WM8990REGISTERDEFS_H__ */
+/*------------------------------ END OF FILE ---------------------------------*/
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 76c1e2d..9fc8edd 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -9,9 +9,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    4th Feb 2006   Initial version.
  */
 
 #include <linux/init.h>
@@ -25,6 +22,7 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include "wm9712.h"
 
 #define WM9712_VERSION "0.4"
 
@@ -351,7 +349,7 @@
 SND_SOC_DAPM_INPUT("MIC2"),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* virtual mixer - mixes left & right channels for spk and mono */
 	{"AC97 Mixer", NULL, "Left DAC"},
 	{"AC97 Mixer", NULL, "Right DAC"},
@@ -446,21 +444,14 @@
 	{"Speaker PGA", NULL, "Speaker Mux"},
 	{"LOUT2", NULL, "Speaker PGA"},
 	{"ROUT2", NULL, "Speaker PGA"},
-
-	{NULL, NULL, NULL},
 };
 
 static int wm9712_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
+	snd_soc_dapm_new_controls(codec, wm9712_dapm_widgets,
+				  ARRAY_SIZE(wm9712_dapm_widgets));
 
-	for (i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
-
-	/* set up audio path connects */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-					   audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -541,7 +532,7 @@
 		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
 		SNDRV_PCM_RATE_48000)
 
-struct snd_soc_codec_dai wm9712_dai[] = {
+struct snd_soc_dai wm9712_dai[] = {
 {
 	.name = "AC97 HiFi",
 	.type = SND_SOC_DAI_AC97_BUS,
@@ -574,23 +565,23 @@
 };
 EXPORT_SYMBOL_GPL(wm9712_dai);
 
-static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9712_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		ac97_write(codec, AC97_POWERDOWN, 0x0000);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		/* disable everything including AC link */
 		ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
 		ac97_write(codec, AC97_POWERDOWN, 0xffff);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -598,12 +589,12 @@
 {
 	if (try_warm && soc_ac97_ops.warm_reset) {
 		soc_ac97_ops.warm_reset(codec->ac97);
-		if (!(ac97_read(codec, 0) & 0x8000))
+		if (ac97_read(codec, 0) == wm9712_reg[0])
 			return 1;
 	}
 
 	soc_ac97_ops.reset(codec->ac97);
-	if (ac97_read(codec, 0) & 0x8000)
+	if (ac97_read(codec, 0) != wm9712_reg[0])
 		goto err;
 	return 0;
 
@@ -618,7 +609,7 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->codec;
 
-	wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+	wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
 
@@ -635,7 +626,7 @@
 		return ret;
 	}
 
-	wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	if (ret == 0) {
 		/* Sync reg_cache with the hardware after cold reset */
@@ -647,8 +638,8 @@
 		}
 	}
 
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
-		wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+		wm9712_set_bias_level(codec, SND_SOC_BIAS_ON);
 
 	return ret;
 }
@@ -682,7 +673,7 @@
 	codec->num_dai = ARRAY_SIZE(wm9712_dai);
 	codec->write = ac97_write;
 	codec->read = ac97_read;
-	codec->dapm_event = wm9712_dapm_event;
+	codec->set_bias_level = wm9712_set_bias_level;
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
@@ -706,7 +697,7 @@
 	/* set alc mux to none */
 	ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
 
-	wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm9712_add_controls(codec);
 	wm9712_add_widgets(codec);
 	ret = snd_soc_register_card(socdev);
diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h
index 719105d..d29e8a1 100644
--- a/sound/soc/codecs/wm9712.h
+++ b/sound/soc/codecs/wm9712.h
@@ -8,7 +8,7 @@
 #define WM9712_DAI_AC97_HIFI	0
 #define WM9712_DAI_AC97_AUX		1
 
-extern struct snd_soc_codec_dai wm9712_dai[2];
+extern struct snd_soc_dai wm9712_dai[2];
 extern struct snd_soc_codec_device soc_codec_dev_wm9712;
 
 #endif
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 1f24116..38d1fe0 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -10,9 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    4th Feb 2006   Initial version.
- *
  *  Features:-
  *
  *   o Support for AC97 Codec, Voice DAC and Aux DAC
@@ -456,7 +453,7 @@
 SND_SOC_DAPM_VMID("VMID"),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* left HP mixer */
 	{"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
 	{"Left HP Mixer", "Voice Playback Switch",   "Voice DAC"},
@@ -607,21 +604,14 @@
 	{"Capture Mono Mux", "Stereo", "Capture Mixer"},
 	{"Capture Mono Mux", "Left", "Left Capture Source"},
 	{"Capture Mono Mux", "Right", "Right Capture Source"},
-
-	{NULL, NULL, NULL},
 };
 
 static int wm9713_add_widgets(struct snd_soc_codec *codec)
 {
-	int i;
+	snd_soc_dapm_new_controls(codec, wm9713_dapm_widgets,
+				  ARRAY_SIZE(wm9713_dapm_widgets));
 
-	for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
-
-	/* set up audio path audio_mapnects */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	snd_soc_dapm_new_widgets(codec);
 	return 0;
@@ -799,7 +789,7 @@
 	return 0;
 }
 
-static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
 		int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -810,7 +800,7 @@
  * Tristate the PCM DAI lines, tristate can be disabled by calling
  * wm9713_set_dai_fmt()
  */
-static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
 	int tristate)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -826,7 +816,7 @@
  * Configure WM9713 clock dividers.
  * Voice DAC needs 256 FS
  */
-static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 		int div_id, int div)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -868,7 +858,7 @@
 	return 0;
 }
 
-static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
+static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -886,7 +876,7 @@
 		gpio |= 0x0018;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFS:
-		reg |= 0x0200;
+		reg |= 0x2000;
 		gpio |= 0x001a;
 		break;
 	case SND_SOC_DAIFMT_CBS_CFM:
@@ -1011,15 +1001,24 @@
 	return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
 }
 
-#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
-		SNDRV_PCM_RATE_48000)
+#define WM9713_RATES (SNDRV_PCM_RATE_8000  |	\
+		      SNDRV_PCM_RATE_11025 |	\
+		      SNDRV_PCM_RATE_22050 |	\
+		      SNDRV_PCM_RATE_44100 |	\
+		      SNDRV_PCM_RATE_48000)
+
+#define WM9713_PCM_RATES (SNDRV_PCM_RATE_8000  |	\
+			  SNDRV_PCM_RATE_11025 |	\
+			  SNDRV_PCM_RATE_16000 |	\
+			  SNDRV_PCM_RATE_22050 |	\
+			  SNDRV_PCM_RATE_44100 |	\
+			  SNDRV_PCM_RATE_48000)
 
 #define WM9713_PCM_FORMATS \
 	(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
 	 SNDRV_PCM_FORMAT_S24_LE)
 
-struct snd_soc_codec_dai wm9713_dai[] = {
+struct snd_soc_dai wm9713_dai[] = {
 {
 	.name = "AC97 HiFi",
 	.type = SND_SOC_DAI_AC97_BUS,
@@ -1061,13 +1060,13 @@
 		.stream_name = "Voice Playback",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = WM9713_RATES,
+		.rates = WM9713_PCM_RATES,
 		.formats = WM9713_PCM_FORMATS,},
 	.capture = {
 		.stream_name = "Voice Capture",
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = WM9713_RATES,
+		.rates = WM9713_PCM_RATES,
 		.formats = WM9713_PCM_FORMATS,},
 	.ops = {
 		.hw_params = wm9713_pcm_hw_params,
@@ -1086,44 +1085,44 @@
 {
 	if (try_warm && soc_ac97_ops.warm_reset) {
 		soc_ac97_ops.warm_reset(codec->ac97);
-		if (!(ac97_read(codec, 0) & 0x8000))
+		if (ac97_read(codec, 0) == wm9713_reg[0])
 			return 1;
 	}
 
 	soc_ac97_ops.reset(codec->ac97);
-	if (ac97_read(codec, 0) & 0x8000)
+	if (ac97_read(codec, 0) != wm9713_reg[0])
 		return -EIO;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(wm9713_reset);
 
-static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9713_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
 {
 	u16 reg;
 
-	switch (event) {
-	case SNDRV_CTL_POWER_D0: /* full On */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
 		/* enable thermal shutdown */
 		reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
 		ac97_write(codec, AC97_EXTENDED_MID, reg);
 		break;
-	case SNDRV_CTL_POWER_D1: /* partial On */
-	case SNDRV_CTL_POWER_D2: /* partial On */
+	case SND_SOC_BIAS_PREPARE:
 		break;
-	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+	case SND_SOC_BIAS_STANDBY:
 		/* enable master bias and vmid */
 		reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
 		ac97_write(codec, AC97_EXTENDED_MID, reg);
 		ac97_write(codec, AC97_POWERDOWN, 0x0000);
 		break;
-	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+	case SND_SOC_BIAS_OFF:
 		/* disable everything including AC link */
 		ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
 		ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
 		ac97_write(codec, AC97_POWERDOWN, 0xffff);
 		break;
 	}
-	codec->dapm_state = event;
+	codec->bias_level = level;
 	return 0;
 }
 
@@ -1160,7 +1159,7 @@
 		return ret;
 	}
 
-	wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* do we need to re-start the PLL ? */
 	if (wm9713->pll_out)
@@ -1176,8 +1175,8 @@
 		}
 	}
 
-	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
-		wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+		wm9713_set_bias_level(codec, SND_SOC_BIAS_ON);
 
 	return ret;
 }
@@ -1216,7 +1215,7 @@
 	codec->num_dai = ARRAY_SIZE(wm9713_dai);
 	codec->write = ac97_write;
 	codec->read = ac97_read;
-	codec->dapm_event = wm9713_dapm_event;
+	codec->set_bias_level = wm9713_set_bias_level;
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
@@ -1238,7 +1237,7 @@
 		goto reset_err;
 	}
 
-	wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* unmute the adc - move to kcontrol */
 	reg = ac97_read(codec, AC97_CD) & 0x7fff;
diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h
index d357b6c..63b8d81 100644
--- a/sound/soc/codecs/wm9713.h
+++ b/sound/soc/codecs/wm9713.h
@@ -46,7 +46,7 @@
 #define WM9713_DAI_PCM_VOICE	2
 
 extern struct snd_soc_codec_device soc_codec_dev_wm9713;
-extern struct snd_soc_codec_dai wm9713_dai[3];
+extern struct snd_soc_dai wm9713_dai[3];
 
 int wm9713_reset(struct snd_soc_codec *codec,  int try_warm);
 
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 20680c5..8f7e338 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -1,6 +1,6 @@
 config SND_DAVINCI_SOC
 	tristate "SoC Audio for the TI DAVINCI chip"
-	depends on ARCH_DAVINCI && SND_SOC
+	depends on ARCH_DAVINCI
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the DAVINCI AC97 or I2S interface. You will also need
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index fcd1652..5e2c306 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -33,24 +33,24 @@
 			 struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int ret = 0;
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 					 SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
 				       SND_SOC_DAIFMT_IB_NF);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK,
 					    SND_SOC_CLOCK_OUT);
 	if (ret < 0)
 		return ret;
@@ -71,7 +71,7 @@
 };
 
 /* davinci-evm machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	/* Headphone connected to HPLOUT, HPROUT */
 	{"Headphone Jack", NULL, "HPLOUT"},
 	{"Headphone Jack", NULL, "HPROUT"},
@@ -90,36 +90,30 @@
 	{"LINE2L", NULL, "Line In"},
 	{"LINE1R", NULL, "Line In"},
 	{"LINE2R", NULL, "Line In"},
-
-	{NULL, NULL, NULL},
 };
 
 /* Logic for a aic3x as connected on a davinci-evm */
 static int evm_aic3x_init(struct snd_soc_codec *codec)
 {
-	int i;
-
 	/* Add davinci-evm specific widgets */
-	for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+				  ARRAY_SIZE(aic3x_dapm_widgets));
 
 	/* Set up davinci-evm specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-					   audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
 	/* not connected */
-	snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+	snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
+	snd_soc_dapm_disable_pin(codec, "HPLCOM");
+	snd_soc_dapm_disable_pin(codec, "HPRCOM");
 
 	/* always connected */
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
-	snd_soc_dapm_set_endpoint(codec, "Line Out", 1);
-	snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
-	snd_soc_dapm_set_endpoint(codec, "Line In", 1);
+	snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+	snd_soc_dapm_enable_pin(codec, "Line Out");
+	snd_soc_dapm_enable_pin(codec, "Mic Jack");
+	snd_soc_dapm_enable_pin(codec, "Line In");
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index c421774..5ebf1ff 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -147,7 +147,7 @@
 static int davinci_i2s_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
 
 	cpu_dai->dma_data = dev->dma_params[substream->stream];
@@ -155,7 +155,7 @@
 	return 0;
 }
 
-static int davinci_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 				   unsigned int fmt)
 {
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
@@ -295,11 +295,12 @@
 	return ret;
 }
 
-static int davinci_i2s_probe(struct platform_device *pdev)
+static int davinci_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_machine *machine = socdev->machine;
-	struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+	struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
 	struct davinci_mcbsp_dev *dev;
 	struct resource *mem, *ioarea;
 	struct evm_snd_platform_data *pdata;
@@ -356,11 +357,12 @@
 	return ret;
 }
 
-static void davinci_i2s_remove(struct platform_device *pdev)
+static void davinci_i2s_remove(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_machine *machine = socdev->machine;
-	struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+	struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	struct resource *mem;
 
@@ -376,7 +378,7 @@
 
 #define DAVINCI_I2S_RATES	SNDRV_PCM_RATE_8000_96000
 
-struct snd_soc_cpu_dai davinci_i2s_dai = {
+struct snd_soc_dai davinci_i2s_dai = {
 	.name = "davinci-i2s",
 	.id = 0,
 	.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h
index 9592d17..c5b0918 100644
--- a/sound/soc/davinci/davinci-i2s.h
+++ b/sound/soc/davinci/davinci-i2s.h
@@ -12,6 +12,6 @@
 #ifndef _DAVINCI_I2S_H
 #define _DAVINCI_I2S_H
 
-extern struct snd_soc_cpu_dai davinci_i2s_dai;
+extern struct snd_soc_dai davinci_i2s_dai;
 
 #endif
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 6a76927..6a5e56a 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -350,7 +350,7 @@
 static u64 davinci_pcm_dmamask = 0xffffffff;
 
 static int davinci_pcm_new(struct snd_card *card,
-			   struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+			   struct snd_soc_dai *dai, struct snd_pcm *pcm)
 {
 	int ret;
 
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 257101f..3368ace 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,8 +1,6 @@
-menu "ALSA SoC audio for Freescale SOCs"
-
 config SND_SOC_MPC8610
 	bool "ALSA SoC support for the MPC8610 SOC"
-	depends on SND_SOC && MPC8610_HPCD
+	depends on MPC8610_HPCD
 	default y if MPC8610
 	help
 	  Say Y if you want to add support for codecs attached to the SSI
@@ -16,5 +14,3 @@
 	default y if MPC8610_HPCD
 	help
 	  Say Y if you want to enable audio on the Freescale MPC8610 HPCD.
-
-endmenu
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 78de716..da2bc59 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -282,7 +282,7 @@
  * once for each .dai_link in the machine driver's snd_soc_machine
  * structure.
  */
-static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
 	struct snd_pcm *pcm)
 {
 	static u64 fsl_dma_dmamask = DMA_BIT_MASK(32);
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h
index 430a6ce..385d4a4 100644
--- a/sound/soc/fsl/fsl_dma.h
+++ b/sound/soc/fsl/fsl_dma.h
@@ -126,7 +126,7 @@
 	u8 res[4];      /* Reserved */
 } __attribute__ ((aligned(32), packed));
 
-/* DMA information needed to create a snd_soc_cpu_dai object
+/* DMA information needed to create a snd_soc_dai object
  *
  * ssi_stx_phys: bus address of SSI STX register to use
  * ssi_srx_phys: bus address of SSI SRX register to use
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index f588545..71bff33 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -82,7 +82,7 @@
 	struct device *dev;
 	unsigned int playback;
 	unsigned int capture;
-	struct snd_soc_cpu_dai cpu_dai;
+	struct snd_soc_dai cpu_dai;
 	struct device_attribute dev_attr;
 
 	struct {
@@ -479,7 +479,7 @@
  * @freq: the frequency of the given clock ID, currently ignored
  * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
  */
-static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int fsl_ssi_set_sysclk(struct snd_soc_dai *cpu_dai,
 			      int clk_id, unsigned int freq, int dir)
 {
 
@@ -497,7 +497,7 @@
  *
  * @format: one of SND_SOC_DAIFMT_xxx
  */
-static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format)
+static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
 {
 	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
 }
@@ -505,7 +505,7 @@
 /**
  * fsl_ssi_dai_template: template CPU DAI for the SSI
  */
-static struct snd_soc_cpu_dai fsl_ssi_dai_template = {
+static struct snd_soc_dai fsl_ssi_dai_template = {
 	.playback = {
 		/* The SSI does not support monaural audio. */
 		.channels_min = 2,
@@ -569,15 +569,15 @@
 }
 
 /**
- * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure
+ * fsl_ssi_create_dai: create a snd_soc_dai structure
  *
- * This function is called by the machine driver to create a snd_soc_cpu_dai
+ * This function is called by the machine driver to create a snd_soc_dai
  * structure.  The function creates an ssi_private object, which contains
- * the snd_soc_cpu_dai.  It also creates the sysfs statistics device.
+ * the snd_soc_dai.  It also creates the sysfs statistics device.
  */
-struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
+struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
 {
-	struct snd_soc_cpu_dai *fsl_ssi_dai;
+	struct snd_soc_dai *fsl_ssi_dai;
 	struct fsl_ssi_private *ssi_private;
 	int ret = 0;
 	struct device_attribute *dev_attr;
@@ -588,7 +588,7 @@
 		return NULL;
 	}
 	memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
-	       sizeof(struct snd_soc_cpu_dai));
+	       sizeof(struct snd_soc_dai));
 
 	fsl_ssi_dai = &ssi_private->cpu_dai;
 	dev_attr = &ssi_private->dev_attr;
@@ -623,11 +623,11 @@
 EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
 
 /**
- * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object
+ * fsl_ssi_destroy_dai: destroy the snd_soc_dai object
  *
  * This function undoes the operations of fsl_ssi_create_dai()
  */
-void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai)
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
 {
 	struct fsl_ssi_private *ssi_private =
 	container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index c5ce88e..83b44d7 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -217,8 +217,8 @@
 	struct device *dev;
 };
 
-struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
-void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai);
+struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);
+void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai);
 
 #endif
 
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index a00aac7..4bdc9d8 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -58,9 +58,9 @@
 		sound_device->dev.platform_data;
 
 	/* Program the signal routing between the SSI and the DMA */
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI);
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI);
 
 	guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,
@@ -96,62 +96,52 @@
 static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct mpc8610_hpcd_data *machine_data =
 		rtd->socdev->dev->platform_data;
 	int ret = 0;
 
 	/* Tell the CPU driver what the serial protocol is. */
-	if (cpu_dai->dai_ops.set_fmt) {
-		ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
-			machine_data->dai_format);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set CPU driver audio format\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set CPU driver audio format\n");
+		return ret;
 	}
 
 	/* Tell the codec driver what the serial protocol is. */
-	if (codec_dai->dai_ops.set_fmt) {
-		ret = codec_dai->dai_ops.set_fmt(codec_dai,
-			machine_data->dai_format);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set codec driver audio format\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set codec driver audio format\n");
+		return ret;
 	}
 
 	/*
 	 * Tell the CPU driver what the clock frequency is, and whether it's a
 	 * slave or master.
 	 */
-	if (cpu_dai->dai_ops.set_sysclk) {
-		ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, 0,
-			machine_data->clk_frequency,
-			machine_data->cpu_clk_direction);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set CPU driver clock parameters\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_sysclk(cpu_dai, 0,
+					machine_data->clk_frequency,
+					machine_data->cpu_clk_direction);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set CPU driver clock parameters\n");
+		return ret;
 	}
 
 	/*
 	 * Tell the codec driver what the MCLK frequency is, and whether it's
 	 * a slave or master.
 	 */
-	if (codec_dai->dai_ops.set_sysclk) {
-		ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0,
-			machine_data->clk_frequency,
-			machine_data->codec_clk_direction);
-		if (ret < 0) {
-			dev_err(substream->pcm->card->dev,
-				"could not set codec driver clock params\n");
-			return ret;
-		}
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+					machine_data->clk_frequency,
+					machine_data->codec_clk_direction);
+	if (ret < 0) {
+		dev_err(substream->pcm->card->dev,
+			"could not set codec driver clock params\n");
+		return ret;
 	}
 
 	return 0;
@@ -170,9 +160,9 @@
 
 	/* Restore the signal routing */
 
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[0], 0);
-	guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1,
+	guts_set_dmacr(machine_data->guts, machine_data->dma_id,
 		machine_data->dma_channel_id[1], 0);
 
 	switch (machine_data->ssi_id) {
@@ -182,7 +172,7 @@
 		break;
 	case 1:
 		clrsetbits_be32(&machine_data->guts->pmuxcr,
-			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA);
+			CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA);
 		break;
 	}
 
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 0230d83..aea27e7 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -1,5 +1,3 @@
-menu "SoC Audio for the Texas Instruments OMAP"
-
 config SND_OMAP_SOC
 	tristate "SoC Audio for the Texas Instruments OMAP chips"
 	depends on ARCH_OMAP && SND_SOC
@@ -15,5 +13,3 @@
 	select SND_SOC_TLV320AIC3X
 	help
 	  Say Y if you want to add support for SoC audio on Nokia N810.
-
-endmenu
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 6533563..02cec968 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -30,15 +30,15 @@
 
 #include <asm/mach-types.h>
 #include <asm/arch/hardware.h>
-#include <asm/arch/gpio.h>
+#include <linux/gpio.h>
 #include <asm/arch/mcbsp.h>
 
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
 #include "../codecs/tlv320aic3x.h"
 
-#define RX44_HEADSET_AMP_GPIO	10
-#define RX44_SPEAKER_AMP_GPIO	101
+#define N810_HEADSET_AMP_GPIO	10
+#define N810_SPEAKER_AMP_GPIO	101
 
 static struct clk *sys_clkout2;
 static struct clk *sys_clkout2_src;
@@ -46,13 +46,26 @@
 
 static int n810_spk_func;
 static int n810_jack_func;
+static int n810_dmic_func;
 
 static void n810_ext_control(struct snd_soc_codec *codec)
 {
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", n810_spk_func);
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", n810_jack_func);
+	if (n810_spk_func)
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	else
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
 
-	snd_soc_dapm_sync_endpoints(codec);
+	if (n810_jack_func)
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+	else
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+	if (n810_dmic_func)
+		snd_soc_dapm_enable_pin(codec, "DMic");
+	else
+		snd_soc_dapm_disable_pin(codec, "DMic");
+
+	snd_soc_dapm_sync(codec);
 }
 
 static int n810_startup(struct snd_pcm_substream *substream)
@@ -73,12 +86,12 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	int err;
 
 	/* Set codec DAI configuration */
-	err = codec_dai->dai_ops.set_fmt(codec_dai,
+	err = snd_soc_dai_set_fmt(codec_dai,
 					 SND_SOC_DAIFMT_I2S |
 					 SND_SOC_DAIFMT_NB_NF |
 					 SND_SOC_DAIFMT_CBM_CFM);
@@ -86,7 +99,7 @@
 		return err;
 
 	/* Set cpu DAI configuration */
-	err = cpu_dai->dai_ops.set_fmt(cpu_dai,
+	err = snd_soc_dai_set_fmt(cpu_dai,
 				       SND_SOC_DAIFMT_I2S |
 				       SND_SOC_DAIFMT_NB_NF |
 				       SND_SOC_DAIFMT_CBM_CFM);
@@ -94,7 +107,7 @@
 		return err;
 
 	/* Set the codec system clock for DAC and ADC */
-	err = codec_dai->dai_ops.set_sysclk(codec_dai, 0, 12000000,
+	err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
 					    SND_SOC_CLOCK_IN);
 
 	return err;
@@ -150,13 +163,35 @@
 	return 1;
 }
 
+static int n810_get_input(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = n810_dmic_func;
+
+	return 0;
+}
+
+static int n810_set_input(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+	if (n810_dmic_func == ucontrol->value.integer.value[0])
+		return 0;
+
+	n810_dmic_func = ucontrol->value.integer.value[0];
+	n810_ext_control(codec);
+
+	return 1;
+}
+
 static int n810_spk_event(struct snd_soc_dapm_widget *w,
 			  struct snd_kcontrol *k, int event)
 {
 	if (SND_SOC_DAPM_EVENT_ON(event))
-		omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 1);
+		gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
 	else
-		omap_set_gpio_dataout(RX44_SPEAKER_AMP_GPIO, 0);
+		gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
 
 	return 0;
 }
@@ -165,9 +200,9 @@
 			   struct snd_kcontrol *k, int event)
 {
 	if (SND_SOC_DAPM_EVENT_ON(event))
-		omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 1);
+		gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
 	else
-		omap_set_gpio_dataout(RX44_HEADSET_AMP_GPIO, 0);
+		gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
 
 	return 0;
 }
@@ -175,21 +210,27 @@
 static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
 	SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
 	SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
+	SND_SOC_DAPM_MIC("DMic", NULL),
 };
 
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 	{"Headphone Jack", NULL, "HPLOUT"},
 	{"Headphone Jack", NULL, "HPROUT"},
 
 	{"Ext Spk", NULL, "LLOUT"},
 	{"Ext Spk", NULL, "RLOUT"},
+
+	{"DMic Rate 64", NULL, "Mic Bias 2V"},
+	{"Mic Bias 2V", NULL, "DMic"},
 };
 
 static const char *spk_function[] = {"Off", "On"};
 static const char *jack_function[] = {"Off", "Headphone"};
+static const char *input_function[] = {"ADC", "Digital Mic"};
 static const struct soc_enum n810_enum[] = {
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
 };
 
 static const struct snd_kcontrol_new aic33_n810_controls[] = {
@@ -197,6 +238,8 @@
 		     n810_get_spk, n810_set_spk),
 	SOC_ENUM_EXT("Jack Function", n810_enum[1],
 		     n810_get_jack, n810_set_jack),
+	SOC_ENUM_EXT("Input Select",  n810_enum[2],
+		     n810_get_input, n810_set_input),
 };
 
 static int n810_aic33_init(struct snd_soc_codec *codec)
@@ -204,9 +247,9 @@
 	int i, err;
 
 	/* Not connected */
-	snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0);
-	snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0);
+	snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
+	snd_soc_dapm_disable_pin(codec, "HPLCOM");
+	snd_soc_dapm_disable_pin(codec, "HPRCOM");
 
 	/* Add N810 specific controls */
 	for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
@@ -217,15 +260,13 @@
 	}
 
 	/* Add N810 specific widgets */
-	for (i = 0; i < ARRAY_SIZE(aic33_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &aic33_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, aic33_dapm_widgets,
+				  ARRAY_SIZE(aic33_dapm_widgets));
 
 	/* Set up N810 specific audio path audio_map */
-	for (i = 0; i < ARRAY_SIZE(audio_map); i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
@@ -250,6 +291,8 @@
 /* Audio private data */
 static struct aic3x_setup_data n810_aic33_setup = {
 	.i2c_address = 0x18,
+	.gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
+	.gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
 };
 
 /* Audio subsystem */
@@ -267,7 +310,7 @@
 	int err;
 	struct device *dev;
 
-	if (!machine_is_nokia_n810())
+	if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
 		return -ENODEV;
 
 	n810_snd_device = platform_device_alloc("soc-audio", -1);
@@ -305,12 +348,12 @@
 	clk_set_parent(sys_clkout2_src, func96m_clk);
 	clk_set_rate(sys_clkout2, 12000000);
 
-	if (omap_request_gpio(RX44_HEADSET_AMP_GPIO) < 0)
+	if (gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0)
 		BUG();
-	if (omap_request_gpio(RX44_SPEAKER_AMP_GPIO) < 0)
+	if (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0)
 		BUG();
-	omap_set_gpio_direction(RX44_HEADSET_AMP_GPIO, 0);
-	omap_set_gpio_direction(RX44_SPEAKER_AMP_GPIO, 0);
+	gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
+	gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
 
 	return 0;
 err2:
@@ -325,6 +368,9 @@
 
 static void __exit n810_soc_exit(void)
 {
+	gpio_free(N810_SPEAKER_AMP_GPIO);
+	gpio_free(N810_HEADSET_AMP_GPIO);
+
 	platform_device_unregister(n810_snd_device);
 }
 
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 40d87e6..00b0c9d 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -103,7 +103,7 @@
 static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 	int err = 0;
 
@@ -116,7 +116,7 @@
 static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 
 	if (!cpu_dai->active) {
@@ -128,7 +128,7 @@
 static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 	int err = 0;
 
@@ -157,7 +157,7 @@
 				    struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 	struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
 	int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
@@ -223,7 +223,7 @@
  * This must be called before _set_clkdiv and _set_sysclk since McBSP register
  * cache is initialized here
  */
-static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 				      unsigned int fmt)
 {
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -292,7 +292,7 @@
 	return 0;
 }
 
-static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
 				     int div_id, int div)
 {
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
@@ -347,7 +347,7 @@
 	return 0;
 }
 
-static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 					 int clk_id, unsigned int freq,
 					 int dir)
 {
@@ -376,7 +376,7 @@
 	return err;
 }
 
-struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS] = {
+struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS] = {
 {
 	.name = "omap-mcbsp-dai",
 	.id = 0,
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 9965fd4..ed8afb5 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -44,6 +44,6 @@
  */
 #define NUM_LINKS	1
 
-extern struct snd_soc_cpu_dai omap_mcbsp_dai[NUM_LINKS];
+extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS];
 
 #endif
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 6237020..e092f3d 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -316,7 +316,7 @@
 	}
 }
 
-int omap_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
 		 struct snd_pcm *pcm)
 {
 	int ret = 0;
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 484f883..12f6ac9 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,6 +1,6 @@
 config SND_PXA2XX_SOC
 	tristate "SoC Audio for the Intel PXA2xx chip"
-	depends on ARCH_PXA && SND_SOC
+	depends on ARCH_PXA
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the PXA2xx AC97, I2S or SSP interface. You will also need
@@ -62,3 +62,12 @@
 	help
 	  Say Y if you want to add support for SoC audio on the
 	  Toshiba e800 PDA
+
+config SND_PXA2XX_SOC_EM_X270
+	tristate "SoC Audio support for CompuLab EM-x270"
+	depends on SND_PXA2XX_SOC && MACH_EM_X270
+	select SND_PXA2XX_SOC_AC97
+	select SND_SOC_WM9712
+	help
+	  Say Y if you want to add support for SoC audio on
+	  CompuLab EM-x270.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 04e5646..5bc8edf 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -13,10 +13,11 @@
 snd-soc-tosa-objs := tosa.o
 snd-soc-e800-objs := e800_wm9712.o
 snd-soc-spitz-objs := spitz.o
+snd-soc-em-x270-objs := em-x270.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
 obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
 obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
 obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
-
+obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 7f32a11..c029446 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -11,10 +11,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    30th Nov 2005   Initial version.
- *
  */
 
 #include <linux/module.h>
@@ -54,47 +50,51 @@
 
 static void corgi_ext_control(struct snd_soc_codec *codec)
 {
-	int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
-
 	/* set up jack connection */
 	switch (corgi_jack_func) {
 	case CORGI_HP:
-		hp = 1;
 		/* set = unmute headphone */
 		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case CORGI_MIC:
-		mic = 1;
 		/* reset = mute headphone */
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case CORGI_LINE:
-		line = 1;
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_enable_pin(codec, "Line Jack");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case CORGI_HEADSET:
-		hs = 1;
-		mic = 1;
 		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
 		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_enable_pin(codec, "Headset Jack");
 		break;
 	}
 
 	if (corgi_spk_func == CORGI_SPK_ON)
-		spk = 1;
-
-	/* set the enpoints to their new connetion states */
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
-	snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
-	snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
-	snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	else
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
 
 	/* signal a DAPM event */
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int corgi_startup(struct snd_pcm_substream *substream)
@@ -123,8 +123,8 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	unsigned int clk = 0;
 	int ret = 0;
 
@@ -143,25 +143,25 @@
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set the I2S system clock as input (unused) */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -247,7 +247,7 @@
 };
 
 /* Corgi machine audio map (connections to the codec pins) */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headset Jack  - in = micin, out = LHPOUT*/
 	{"Headset Jack", NULL, "LHPOUT"},
@@ -265,8 +265,6 @@
 
 	/* Same as the above but no mic bias for line signals */
 	{"MICIN", NULL, "Line Jack"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -291,8 +289,8 @@
 {
 	int i, err;
 
-	snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
+	snd_soc_dapm_disable_pin(codec, "LLINEIN");
+	snd_soc_dapm_disable_pin(codec, "RLINEIN");
 
 	/* Add corgi specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
@@ -303,15 +301,13 @@
 	}
 
 	/* Add corgi specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+				  ARRAY_SIZE(wm8731_dapm_widgets));
 
 	/* Set up corgi specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
new file mode 100644
index 0000000..02dcac3
--- /dev/null
+++ b/sound/soc/pxa/em-x270.c
@@ -0,0 +1,102 @@
+/*
+ * em-x270.c  --  SoC audio for EM-X270
+ *
+ * Copyright 2007 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copied from tosa.c:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *          Richard Purdie <richard@openedhand.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static struct snd_soc_dai_link em_x270_dai[] = {
+	{
+		.name = "AC97",
+		.stream_name = "AC97 HiFi",
+		.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+		.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+	},
+	{
+		.name = "AC97 Aux",
+		.stream_name = "AC97 Aux",
+		.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+		.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+	},
+};
+
+static struct snd_soc_machine em_x270 = {
+	.name = "EM-X270",
+	.dai_link = em_x270_dai,
+	.num_links = ARRAY_SIZE(em_x270_dai),
+};
+
+static struct snd_soc_device em_x270_snd_devdata = {
+	.machine = &em_x270,
+	.platform = &pxa2xx_soc_platform,
+	.codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *em_x270_snd_device;
+
+static int __init em_x270_init(void)
+{
+	int ret;
+
+	if (!machine_is_em_x270())
+		return -ENODEV;
+
+	em_x270_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!em_x270_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(em_x270_snd_device, &em_x270_snd_devdata);
+	em_x270_snd_devdata.dev = &em_x270_snd_device->dev;
+	ret = platform_device_add(em_x270_snd_device);
+
+	if (ret)
+		platform_device_put(em_x270_snd_device);
+
+	return ret;
+}
+
+static void __exit em_x270_exit(void)
+{
+	platform_device_unregister(em_x270_snd_device);
+}
+
+module_init(em_x270_init);
+module_exit(em_x270_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mike Rapoport");
+MODULE_DESCRIPTION("ALSA SoC EM-X270");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 7e830b2..65a4e9a 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -48,8 +48,6 @@
 
 static void poodle_ext_control(struct snd_soc_codec *codec)
 {
-	int spk = 0;
-
 	/* set up jack connection */
 	if (poodle_jack_func == POODLE_HP) {
 		/* set = unmute headphone */
@@ -57,23 +55,23 @@
 			POODLE_LOCOMO_GPIO_MUTE_L, 1);
 		locomo_gpio_write(&poodle_locomo_device.dev,
 			POODLE_LOCOMO_GPIO_MUTE_R, 1);
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
 	} else {
 		locomo_gpio_write(&poodle_locomo_device.dev,
 			POODLE_LOCOMO_GPIO_MUTE_L, 0);
 		locomo_gpio_write(&poodle_locomo_device.dev,
 			POODLE_LOCOMO_GPIO_MUTE_R, 0);
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
 	}
 
-	if (poodle_spk_func == POODLE_SPK_ON)
-		spk = 1;
-
 	/* set the enpoints to their new connetion states */
-	snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
+	if (poodle_spk_func == POODLE_SPK_ON)
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	else
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
 
 	/* signal a DAPM event */
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int poodle_startup(struct snd_pcm_substream *substream)
@@ -104,8 +102,8 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	unsigned int clk = 0;
 	int ret = 0;
 
@@ -124,25 +122,25 @@
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set the I2S system clock as input (unused) */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -215,8 +213,8 @@
 SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
 };
 
-/* Corgi machine audio_mapnections to the codec pins */
-static const char *audio_map[][3] = {
+/* Corgi machine connections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headphone connected to LHPOUT1, RHPOUT1 */
 	{"Headphone Jack", NULL, "LHPOUT"},
@@ -225,8 +223,6 @@
 	/* speaker connected to LOUT, ROUT */
 	{"Ext Spk", NULL, "ROUT"},
 	{"Ext Spk", NULL, "LOUT"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Off", "Headphone"};
@@ -250,9 +246,9 @@
 {
 	int i, err;
 
-	snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
-	snd_soc_dapm_set_endpoint(codec, "MICIN", 1);
+	snd_soc_dapm_disable_pin(codec, "LLINEIN");
+	snd_soc_dapm_disable_pin(codec, "RLINEIN");
+	snd_soc_dapm_enable_pin(codec, "MICIN");
 
 	/* Add poodle specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
@@ -263,15 +259,13 @@
 	}
 
 	/* Add poodle specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
+				  ARRAY_SIZE(wm8731_dapm_widgets));
 
 	/* Set up poodle specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 97ec2d9..059af81 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -283,7 +283,7 @@
 
 #ifdef CONFIG_PM
 static int pxa2xx_ac97_suspend(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	GCR |= GCR_ACLINK_OFF;
 	clk_disable(ac97_clk);
@@ -291,7 +291,7 @@
 }
 
 static int pxa2xx_ac97_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
 	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
@@ -310,7 +310,8 @@
 #define pxa2xx_ac97_resume	NULL
 #endif
 
-static int pxa2xx_ac97_probe(struct platform_device *pdev)
+static int pxa2xx_ac97_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	int ret;
 
@@ -355,7 +356,8 @@
 	return ret;
 }
 
-static void pxa2xx_ac97_remove(struct platform_device *pdev)
+static void pxa2xx_ac97_remove(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
 {
 	GCR |= GCR_ACLINK_OFF;
 	free_irq(IRQ_AC97, NULL);
@@ -372,7 +374,7 @@
 				struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out;
@@ -386,7 +388,7 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
@@ -400,7 +402,7 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		return -ENODEV;
@@ -418,7 +420,7 @@
  * There is only 1 physical AC97 interface for pxa2xx, but it
  * has extra fifo's that can be used for aux DACs and ADCs.
  */
-struct snd_soc_cpu_dai pxa_ac97_dai[] = {
+struct snd_soc_dai pxa_ac97_dai[] = {
 {
 	.name = "pxa2xx-ac97",
 	.id = 0,
diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h
index b8ccfee..e390de8 100644
--- a/sound/soc/pxa/pxa2xx-ac97.h
+++ b/sound/soc/pxa/pxa2xx-ac97.h
@@ -14,7 +14,7 @@
 #define PXA2XX_DAI_AC97_AUX		1
 #define PXA2XX_DAI_AC97_MIC		2
 
-extern struct snd_soc_cpu_dai pxa_ac97_dai[3];
+extern struct snd_soc_dai pxa_ac97_dai[3];
 
 /* platform data */
 extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 4250710..9c06553 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -9,9 +9,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    12th Aug 2005   Initial version.
  */
 
 #include <linux/init.h>
@@ -80,7 +77,7 @@
 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (!cpu_dai->active) {
 		SACR0 |= SACR0_RST;
@@ -101,7 +98,7 @@
 	return 0;
 }
 
-static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
 	/* interface format */
@@ -127,7 +124,7 @@
 	return 0;
 }
 
-static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 		int clk_id, unsigned int freq, int dir)
 {
 	if (clk_id != PXA2XX_I2S_SYSCLK)
@@ -143,7 +140,7 @@
 				struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
 	pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
@@ -240,7 +237,7 @@
 
 #ifdef CONFIG_PM
 static int pxa2xx_i2s_suspend(struct platform_device *dev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	if (!dai->active)
 		return 0;
@@ -258,7 +255,7 @@
 }
 
 static int pxa2xx_i2s_resume(struct platform_device *pdev,
-	struct snd_soc_cpu_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	if (!dai->active)
 		return 0;
@@ -283,7 +280,7 @@
 		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
 		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 
-struct snd_soc_cpu_dai pxa_i2s_dai = {
+struct snd_soc_dai pxa_i2s_dai = {
 	.name = "pxa2xx-i2s",
 	.id = 0,
 	.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/pxa/pxa2xx-i2s.h b/sound/soc/pxa/pxa2xx-i2s.h
index 4435bd9..e2def44 100644
--- a/sound/soc/pxa/pxa2xx-i2s.h
+++ b/sound/soc/pxa/pxa2xx-i2s.h
@@ -15,6 +15,6 @@
 /* I2S clock */
 #define PXA2XX_I2S_SYSCLK		0
 
-extern struct snd_soc_cpu_dai pxa_i2s_dai;
+extern struct snd_soc_dai pxa_i2s_dai;
 
 #endif
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 01ad7bf..2df03ee 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -330,7 +330,7 @@
 
 static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
 
-int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
+int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
 	struct snd_pcm *pcm)
 {
 	int ret = 0;
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index d8b8372..6438579 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -12,9 +12,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    30th Nov 2005   Initial version.
- *
  */
 
 #include <linux/module.h>
@@ -54,60 +51,60 @@
 static void spitz_ext_control(struct snd_soc_codec *codec)
 {
 	if (spitz_spk_func == SPITZ_SPK_ON)
-		snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+		snd_soc_dapm_enable_pin(codec, "Ext Spk");
 	else
-		snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0);
+		snd_soc_dapm_disable_pin(codec, "Ext Spk");
 
 	/* set up jack connection */
 	switch (spitz_jack_func) {
 	case SPITZ_HP:
 		/* enable and unmute hp jack, disable mic bias */
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
 		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_MIC:
 		/* enable mic jack and bias, mute hp */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_LINE:
 		/* enable line jack, disable mic bias and mute hp */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_enable_pin(codec, "Line Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_HEADSET:
 		/* enable and unmute headset jack enable mic bias, mute L hp */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_enable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
+		snd_soc_dapm_enable_pin(codec, "Headset Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	case SPITZ_HP_OFF:
 
 		/* jack removed, everything off */
-		snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-		snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
+		snd_soc_dapm_disable_pin(codec, "Mic Jack");
+		snd_soc_dapm_disable_pin(codec, "Line Jack");
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
 		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
 		break;
 	}
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int spitz_startup(struct snd_pcm_substream *substream)
@@ -124,8 +121,8 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	unsigned int clk = 0;
 	int ret = 0;
 
@@ -144,25 +141,25 @@
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8750_SYSCLK, clk,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set the I2S system clock as input (unused) */
-	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
@@ -250,7 +247,7 @@
 };
 
 /* Spitz machine audio_map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headphone connected to LOUT1, ROUT1 */
 	{"Headphone Jack", NULL, "LOUT1"},
@@ -269,8 +266,6 @@
 
 	/* line is connected to input 1 - no bias */
 	{"LINPUT1", NULL, "Line Jack"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -296,13 +291,13 @@
 	int i, err;
 
 	/* NC codec pins */
-	snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0);
-	snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "MONO", 0);
+	snd_soc_dapm_disable_pin(codec, "RINPUT1");
+	snd_soc_dapm_disable_pin(codec, "LINPUT2");
+	snd_soc_dapm_disable_pin(codec, "RINPUT2");
+	snd_soc_dapm_disable_pin(codec, "LINPUT3");
+	snd_soc_dapm_disable_pin(codec, "RINPUT3");
+	snd_soc_dapm_disable_pin(codec, "OUT3");
+	snd_soc_dapm_disable_pin(codec, "MONO");
 
 	/* Add spitz specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
@@ -313,15 +308,13 @@
 	}
 
 	/* Add spitz specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets,
+				  ARRAY_SIZE(wm8750_dapm_widgets));
 
-	/* Set up spitz specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++)
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
+	/* Set up spitz specific audio paths */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index 7346d7e..b6edb61 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -12,9 +12,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    30th Nov 2005   Initial version.
- *
  * GPIO's
  *  1 - Jack Insertion
  *  5 - Hookswitch (headset answer/hang up switch)
@@ -55,29 +52,31 @@
 
 static void tosa_ext_control(struct snd_soc_codec *codec)
 {
-	int spk = 0, mic_int = 0, hp = 0, hs = 0;
-
 	/* set up jack connection */
 	switch (tosa_jack_func) {
 	case TOSA_HP:
-		hp = 1;
+		snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case TOSA_MIC_INT:
-		mic_int = 1;
+		snd_soc_dapm_enable_pin(codec, "Mic (Internal)");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_disable_pin(codec, "Headset Jack");
 		break;
 	case TOSA_HEADSET:
-		hs = 1;
+		snd_soc_dapm_disable_pin(codec, "Mic (Internal)");
+		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+		snd_soc_dapm_enable_pin(codec, "Headset Jack");
 		break;
 	}
 
 	if (tosa_spk_func == TOSA_SPK_ON)
-		spk = 1;
+		snd_soc_dapm_enable_pin(codec, "Speaker");
+	else
+		snd_soc_dapm_disable_pin(codec, "Speaker");
 
-	snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
-	snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
-	snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
-	snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 }
 
 static int tosa_startup(struct snd_pcm_substream *substream)
@@ -154,7 +153,7 @@
 };
 
 /* tosa audio map */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route audio_map[] = {
 
 	/* headphone connected to HPOUTL, HPOUTR */
 	{"Headphone Jack", NULL, "HPOUTL"},
@@ -173,8 +172,6 @@
 	{"Headset Jack", NULL, "HPOUTR"},
 	{"LINEINR", NULL, "Mic Bias"},
 	{"Mic Bias", NULL, "Headset Jack"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
@@ -196,8 +193,8 @@
 {
 	int i, err;
 
-	snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
-	snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
+	snd_soc_dapm_disable_pin(codec, "OUT3");
+	snd_soc_dapm_disable_pin(codec, "MONOOUT");
 
 	/* add tosa specific controls */
 	for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
@@ -208,17 +205,13 @@
 	}
 
 	/* add tosa specific widgets */
-	for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
-		snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
-	}
+	snd_soc_dapm_new_controls(codec, tosa_dapm_widgets,
+				  ARRAY_SIZE(tosa_dapm_widgets));
 
 	/* set up tosa specific audio path audio_map */
-	for (i = 0; audio_map[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
-	}
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 1f6dbfc..b9f2353 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,7 +1,6 @@
 config SND_S3C24XX_SOC
 	tristate "SoC Audio for the Samsung S3C24XX chips"
-	depends on ARCH_S3C2410 && SND_SOC
-	select SND_PCM
+	depends on ARCH_S3C2410
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  the S3C24XX AC97, I2S or SSP interface. You will also need
@@ -16,7 +15,6 @@
 config SND_S3C2443_SOC_AC97
 	tristate
 	select AC97_BUS
-	select SND_AC97_CODEC
 	select SND_SOC_AC97_BUS
 	
 config SND_S3C24XX_SOC_NEO1973_WM8753
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 0e9d1c5..4d7a9aa 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -10,10 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    20th Jan 2007   Initial version.
- *    05th Feb 2007   Rename all to Neo1973
- *
  */
 
 #include <linux/module.h>
@@ -26,6 +22,7 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/tlv.h>
 
 #include <asm/mach-types.h>
 #include <asm/hardware/scoop.h>
@@ -43,6 +40,14 @@
 #include "s3c24xx-pcm.h"
 #include "s3c24xx-i2s.h"
 
+/* Debugging stuff */
+#define S3C24XX_SOC_NEO1973_WM8753_DEBUG 0
+#if S3C24XX_SOC_NEO1973_WM8753_DEBUG
+#define DBG(x...) printk(KERN_DEBUG "s3c24xx-soc-neo1973-wm8753: " x)
+#else
+#define DBG(x...)
+#endif
+
 /* define the scenarios */
 #define NEO_AUDIO_OFF			0
 #define NEO_GSM_CALL_AUDIO_HANDSET	1
@@ -61,12 +66,14 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	unsigned int pll_out = 0, bclk = 0;
 	int ret = 0;
 	unsigned long iis_clkrate;
 
+	DBG("Entered %s\n", __func__);
+
 	iis_clkrate = s3c24xx_i2s_get_clockrate();
 
 	switch (params_rate(params)) {
@@ -101,44 +108,44 @@
 	}
 
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai,
+	ret = snd_soc_dai_set_fmt(codec_dai,
 		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 		SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+	ret = snd_soc_dai_set_fmt(cpu_dai,
 		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 		SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set MCLK division for sample rate */
-	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
 		S3C2410_IISMOD_32FS);
 	if (ret < 0)
 		return ret;
 
 	/* set codec BCLK division for sample rate */
-	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
 	if (ret < 0)
 		return ret;
 
 	/* set prescaler division for sample rate */
-	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 		S3C24XX_PRESCALE(4, 4));
 	if (ret < 0)
 		return ret;
 
 	/* codec PLL input is PCLK/4 */
-	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
 		iis_clkrate / 4, pll_out);
 	if (ret < 0)
 		return ret;
@@ -149,10 +156,12 @@
 static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	DBG("Entered %s\n", __func__);
 
 	/* disable the PLL */
-	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
 }
 
 /*
@@ -167,11 +176,13 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 	unsigned int pcmdiv = 0;
 	int ret = 0;
 	unsigned long iis_clkrate;
 
+	DBG("Entered %s\n", __func__);
+
 	iis_clkrate = s3c24xx_i2s_get_clockrate();
 
 	if (params_rate(params) != 8000)
@@ -183,24 +194,24 @@
 
 	/* todo: gg check mode (DSP_B) against CSR datasheet */
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
 	/* set the codec system clock for DAC and ADC */
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 		return ret;
 
 	/* set codec PCM division for sample rate */
-	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
 	if (ret < 0)
 		return ret;
 
 	/* configue and enable PLL for 12.288MHz output */
-	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
 		iis_clkrate / 4, 12288000);
 	if (ret < 0)
 		return ret;
@@ -211,10 +222,12 @@
 static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	DBG("Entered %s\n", __func__);
 
 	/* disable the PLL */
-	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_voice_ops = {
@@ -233,79 +246,81 @@
 
 static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
 {
+	DBG("Entered %s\n", __func__);
+
 	switch (neo1973_scenario) {
 	case NEO_AUDIO_OFF:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_GSM_CALL_AUDIO_HANDSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_enable_pin(codec, "Call Mic");
 		break;
 	case NEO_GSM_CALL_AUDIO_HEADSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line In");
+		snd_soc_dapm_enable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_GSM_CALL_AUDIO_BLUETOOTH:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_enable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_STEREO_TO_SPEAKERS:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_STEREO_TO_HEADPHONES:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_enable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_CAPTURE_HANDSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_enable_pin(codec, "Call Mic");
 		break;
 	case NEO_CAPTURE_HEADSET:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_enable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	case NEO_CAPTURE_BLUETOOTH:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 		break;
 	default:
-		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
-		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
-		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		snd_soc_dapm_disable_pin(codec, "Audio Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+		snd_soc_dapm_disable_pin(codec, "GSM Line In");
+		snd_soc_dapm_disable_pin(codec, "Headset Mic");
+		snd_soc_dapm_disable_pin(codec, "Call Mic");
 	}
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 
 	return 0;
 }
@@ -315,6 +330,8 @@
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 
+	DBG("Entered %s\n", __func__);
+
 	if (neo1973_scenario == ucontrol->value.integer.value[0])
 		return 0;
 
@@ -327,6 +344,8 @@
 
 static void lm4857_write_regs(void)
 {
+	DBG("Entered %s\n", __func__);
+
 	if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
 		printk(KERN_ERR "lm4857: i2c write failed\n");
 }
@@ -338,6 +357,8 @@
 	int shift = (kcontrol->private_value >> 8) & 0x0F;
 	int mask = (kcontrol->private_value >> 16) & 0xFF;
 
+	DBG("Entered %s\n", __func__);
+
 	ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
 	return 0;
 }
@@ -364,6 +385,8 @@
 {
 	u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
 
+	DBG("Entered %s\n", __func__);
+
 	if (value)
 		value -= 5;
 
@@ -376,6 +399,8 @@
 {
 	u8 value = ucontrol->value.integer.value[0];
 
+	DBG("Entered %s\n", __func__);
+
 	if (value)
 		value += 5;
 
@@ -397,8 +422,7 @@
 };
 
 
-/* example machine audio_mapnections */
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route dapm_routes[] = {
 
 	/* Connections to the lm4857 amp */
 	{"Audio Out", NULL, "LOUT1"},
@@ -421,8 +445,6 @@
 
 	/* Connect the ALC pins */
 	{"ACIN", NULL, "ACOP"},
-
-	{NULL, NULL, NULL},
 };
 
 static const char *lm4857_mode[] = {
@@ -453,13 +475,16 @@
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios),
 };
 
+static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
+
 static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
-	SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
-		lm4857_get_reg, lm4857_set_reg),
-	SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
-		lm4857_get_reg, lm4857_set_reg),
-	SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
-		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+	SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg, stereo_tlv),
+	SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg, mono_tlv),
 	SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
 		lm4857_get_mode, lm4857_set_mode),
 	SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
@@ -483,21 +508,23 @@
 {
 	int i, err;
 
+	DBG("Entered %s\n", __func__);
+
 	/* set up NC codec pins */
-	snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
-	snd_soc_dapm_set_endpoint(codec, "OUT3",  0);
-	snd_soc_dapm_set_endpoint(codec, "OUT4",  0);
-	snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
-	snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
+	snd_soc_dapm_disable_pin(codec, "LOUT2");
+	snd_soc_dapm_disable_pin(codec, "ROUT2");
+	snd_soc_dapm_disable_pin(codec, "OUT3");
+	snd_soc_dapm_disable_pin(codec, "OUT4");
+	snd_soc_dapm_disable_pin(codec, "LINE1");
+	snd_soc_dapm_disable_pin(codec, "LINE2");
 
 
 	/* set endpoints to default mode */
 	set_scenario_endpoints(codec, NEO_AUDIO_OFF);
 
 	/* Add neo1973 specific widgets */
-	for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
-		snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
+	snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+				  ARRAY_SIZE(wm8753_dapm_widgets));
 
 	/* add neo1973 specific controls */
 	for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
@@ -508,20 +535,18 @@
 			return err;
 	}
 
-	/* set up neo1973 specific audio path audio_mapnects */
-	for (i = 0; audio_map[i][0] != NULL; i++) {
-		snd_soc_dapm_connect_input(codec, audio_map[i][0],
-			audio_map[i][1], audio_map[i][2]);
-	}
+	/* set up neo1973 specific audio routes */
+	err = snd_soc_dapm_add_routes(codec, dapm_routes,
+				      ARRAY_SIZE(dapm_routes));
 
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
 /*
  * BT Codec DAI
  */
-static struct snd_soc_cpu_dai bt_dai = {
+static struct snd_soc_dai bt_dai = {
 	.name = "Bluetooth",
 	.id = 0,
 	.type = SND_SOC_DAI_PCM,
@@ -583,6 +608,8 @@
 {
 	int ret;
 
+	DBG("Entered %s\n", __func__);
+
 	client_template.adapter = adap;
 	client_template.addr = addr;
 
@@ -606,6 +633,8 @@
 
 static int lm4857_i2c_detach(struct i2c_client *client)
 {
+	DBG("Entered %s\n", __func__);
+
 	i2c_detach_client(client);
 	kfree(client);
 	return 0;
@@ -613,6 +642,8 @@
 
 static int lm4857_i2c_attach(struct i2c_adapter *adap)
 {
+	DBG("Entered %s\n", __func__);
+
 	return i2c_probe(adap, &addr_data, lm4857_amp_probe);
 }
 
@@ -620,6 +651,8 @@
 
 static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
 {
+	DBG("Entered %s\n", __func__);
+
 	dev_dbg(&dev->dev, "lm4857_suspend\n");
 	lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
 	if (lm4857_state) {
@@ -631,6 +664,8 @@
 
 static int lm4857_resume(struct i2c_client *dev)
 {
+	DBG("Entered %s\n", __func__);
+
 	if (lm4857_state) {
 		lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
 		lm4857_write_regs();
@@ -640,6 +675,8 @@
 
 static void lm4857_shutdown(struct i2c_client *dev)
 {
+	DBG("Entered %s\n", __func__);
+
 	dev_dbg(&dev->dev, "lm4857_shutdown\n");
 	lm4857_regs[LM4857_CTRL] &= 0xf0;
 	lm4857_write_regs();
@@ -671,6 +708,8 @@
 {
 	int ret;
 
+	DBG("Entered %s\n", __func__);
+
 	neo1973_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!neo1973_snd_device)
 		return -ENOMEM;
@@ -691,6 +730,8 @@
 
 static void __exit neo1973_exit(void)
 {
+	DBG("Entered %s\n", __func__);
+
 	i2c_del_driver(&lm4857_i2c_driver);
 	platform_device_unregister(neo1973_snd_device);
 }
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index c4a46dd..ee4676e 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -295,7 +295,7 @@
 /*
  * Set S3C2412 I2S DAI format
  */
-static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 			       unsigned int fmt)
 {
 	u32 iismod;
@@ -500,7 +500,7 @@
 /*
  * Set S3C2412 Clock source
  */
-static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 				  int clk_id, unsigned int freq, int dir)
 {
 	u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
@@ -528,7 +528,7 @@
 /*
  * Set S3C2412 Clock dividers
  */
-static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 				  int div_id, int div)
 {
 	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
@@ -601,7 +601,8 @@
 EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
 
 
-static int s3c2412_i2s_probe(struct platform_device *pdev)
+static int s3c2412_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	DBG("Entered %s\n", __func__);
 
@@ -647,7 +648,7 @@
 
 #ifdef CONFIG_PM
 static int s3c2412_i2s_suspend(struct platform_device *dev,
-			      struct snd_soc_cpu_dai *dai)
+			      struct snd_soc_dai *dai)
 {
 	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 	u32 iismod;
@@ -675,7 +676,7 @@
 }
 
 static int s3c2412_i2s_resume(struct platform_device *pdev,
-			      struct snd_soc_cpu_dai *dai)
+			      struct snd_soc_dai *dai)
 {
 	struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 
@@ -707,7 +708,7 @@
 	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
-struct snd_soc_cpu_dai s3c2412_i2s_dai = {
+struct snd_soc_dai s3c2412_i2s_dai = {
 	.name	= "s3c2412-i2s",
 	.id	= 0,
 	.type	= SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
index 27f48e1..aac08a2 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.h
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -24,7 +24,7 @@
 
 extern struct clk *s3c2412_get_iisclk(void);
 
-extern struct snd_soc_cpu_dai s3c2412_i2s_dai;
+extern struct snd_soc_dai s3c2412_i2s_dai;
 
 struct s3c2412_rate_calc {
 	unsigned int	clk_div;	/* for prescaler */
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index e81d9a6..783349b 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -10,9 +10,6 @@
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
- *
- *  Revision history
- *	21st Mar 2007   Initial Version
  */
 
 #include <linux/init.h>
@@ -212,7 +209,8 @@
 	.dma_size	= 4,
 };
 
-static int s3c2443_ac97_probe(struct platform_device *pdev)
+static int s3c2443_ac97_probe(struct platform_device *pdev,
+			      struct snd_soc_dai *dai)
 {
 	int ret;
 	u32 ac_glbctrl;
@@ -263,7 +261,8 @@
 	return ret;
 }
 
-static void s3c2443_ac97_remove(struct platform_device *pdev)
+static void s3c2443_ac97_remove(struct platform_device *pdev,
+				struct snd_soc_dai *dai)
 {
 	free_irq(IRQ_S3C244x_AC97, NULL);
 	clk_disable(s3c24xx_ac97.ac97_clk);
@@ -275,7 +274,7 @@
 				struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
@@ -317,7 +316,7 @@
 	struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		return -ENODEV;
@@ -353,7 +352,7 @@
 		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
 		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
 
-struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
+struct snd_soc_dai s3c2443_ac97_dai[] = {
 {
 	.name = "s3c2443-ac97",
 	.id = 0,
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
index bf03e8e..a96dcad 100644
--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
+++ b/sound/soc/s3c24xx/s3c24xx-ac97.h
@@ -26,6 +26,6 @@
 #define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
 #endif
 
-extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
+extern struct snd_soc_dai s3c2443_ac97_dai[];
 
 #endif /*S3C24XXAC97_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 1ed6afd..3975242 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -12,11 +12,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *
- *  Revision history
- *    11th Dec 2006   Merged with Simtec driver
- *    10th Nov 2006   Initial version.
  */
 
 #include <linux/init.h>
@@ -180,7 +175,7 @@
 static int s3c24xx_snd_lrsync(void)
 {
 	u32 iiscon;
-	unsigned long timeout = jiffies + msecs_to_jiffies(5);
+	int timeout = 50; /* 5ms */
 
 	DBG("Entered %s\n", __func__);
 
@@ -189,8 +184,9 @@
 		if (iiscon & S3C2410_IISCON_LRINDEX)
 			break;
 
-		if (time_after(jiffies, timeout))
+		if (!timeout--)
 			return -ETIMEDOUT;
+		udelay(100);
 	}
 
 	return 0;
@@ -209,7 +205,7 @@
 /*
  * Set S3C24xx I2S DAI format
  */
-static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 		unsigned int fmt)
 {
 	u32 iismod;
@@ -317,7 +313,7 @@
 /*
  * Set S3C24xx Clock source
  */
-static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 	int clk_id, unsigned int freq, int dir)
 {
 	u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -343,7 +339,7 @@
 /*
  * Set S3C24xx Clock dividers
  */
-static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
+static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 	int div_id, int div)
 {
 	u32 reg;
@@ -381,7 +377,8 @@
 }
 EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
 
-static int s3c24xx_i2s_probe(struct platform_device *pdev)
+static int s3c24xx_i2s_probe(struct platform_device *pdev,
+			     struct snd_soc_dai *dai)
 {
 	DBG("Entered %s\n", __func__);
 
@@ -414,7 +411,7 @@
 
 #ifdef CONFIG_PM
 static int s3c24xx_i2s_suspend(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai)
+		struct snd_soc_dai *cpu_dai)
 {
 	DBG("Entered %s\n", __func__);
 
@@ -429,7 +426,7 @@
 }
 
 static int s3c24xx_i2s_resume(struct platform_device *pdev,
-		struct snd_soc_cpu_dai *cpu_dai)
+		struct snd_soc_dai *cpu_dai)
 {
 	DBG("Entered %s\n", __func__);
 	clk_enable(s3c24xx_i2s.iis_clk);
@@ -452,7 +449,7 @@
 	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
-struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
+struct snd_soc_dai s3c24xx_i2s_dai = {
 	.name = "s3c24xx-i2s",
 	.id = 0,
 	.type = SND_SOC_DAI_I2S,
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.h b/sound/soc/s3c24xx/s3c24xx-i2s.h
index 537b4ec..726d91c 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.h
@@ -32,6 +32,6 @@
 
 u32 s3c24xx_i2s_get_clockrate(void);
 
-extern struct snd_soc_cpu_dai s3c24xx_i2s_dai;
+extern struct snd_soc_dai s3c24xx_i2s_dai;
 
 #endif /*S3C24XXI2S_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 7806ae6..cef79b3 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -12,10 +12,6 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
- *
- *  Revision history
- *    11th Dec 2006   Merged with Simtec driver
- *    10th Nov 2006   Initial version.
  */
 
 #include <linux/module.h>
@@ -433,7 +429,7 @@
 static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK;
 
 static int s3c24xx_pcm_new(struct snd_card *card,
-	struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
+	struct snd_soc_dai *dai, struct snd_pcm *pcm)
 {
 	int ret = 0;
 
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index b4a5630..8515d6f 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -10,9 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    8th Mar 2007   Initial version.
- *
  */
 
 #include <linux/module.h>
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 4c1e013..54bd604 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -3,7 +3,7 @@
 
 config SND_SOC_PCM_SH7760
 	tristate "SoC Audio support for Renesas SH7760"
-	depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
+	depends on CPU_SUBTYPE_SH7760 && SH_DMABRG
 	help
 	  Enable this option for SH7760 AC97/I2S audio support.
 
@@ -13,10 +13,9 @@
 ##
 
 config SND_SOC_SH4_HAC
+	tristate
 	select AC97_BUS
 	select SND_SOC_AC97_BUS
-	select SND_AC97_CODEC
-	tristate
 
 config SND_SOC_SH4_SSI
 	tristate
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index 7a3ce80..9faa126 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -326,7 +326,7 @@
 }
 
 static int camelot_pcm_new(struct snd_card *card,
-			   struct snd_soc_codec_dai *dai,
+			   struct snd_soc_dai *dai,
 			   struct snd_pcm *pcm)
 {
 	/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index b7b676b..df7bc34 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -266,7 +266,7 @@
 #define AC97_FMTS	\
 	SNDRV_PCM_FMTBIT_S16_LE
 
-struct snd_soc_cpu_dai sh4_hac_dai[] = {
+struct snd_soc_dai sh4_hac_dai[] = {
 {
 	.name			= "HAC0",
 	.id			= 0,
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
index 2f91de8..92bfaf4 100644
--- a/sound/soc/sh/sh7760-ac97.c
+++ b/sound/soc/sh/sh7760-ac97.c
@@ -20,12 +20,12 @@
 #define IPSEL 0xFE400034
 
 /* platform specific structs can be declared here */
-extern struct snd_soc_cpu_dai sh4_hac_dai[2];
+extern struct snd_soc_dai sh4_hac_dai[2];
 extern struct snd_soc_platform sh7760_soc_platform;
 
 static int machine_init(struct snd_soc_codec *codec)
 {
-	snd_soc_dapm_sync_endpoints(codec);
+	snd_soc_dapm_sync(codec);
 	return 0;
 }
 
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index 3388bc3..55c3464 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -208,7 +208,7 @@
 	return 0;
 }
 
-static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
+static int ssi_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
 			  unsigned int freq, int dir)
 {
 	struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
@@ -222,7 +222,7 @@
  * This divider is used to generate the SSI_SCK (I2S bitclock) from the
  * clock at the HAC_BIT_CLK ("oversampling clock") pin.
  */
-static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
+static int ssi_set_clkdiv(struct snd_soc_dai *dai, int did, int div)
 {
 	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 	unsigned long ssicr;
@@ -245,7 +245,7 @@
 	return 0;
 }
 
-static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
+static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
 	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
 	unsigned long ssicr = SSIREG(SSICR);
@@ -332,7 +332,7 @@
 	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\
 	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
 
-struct snd_soc_cpu_dai sh4_ssi_dai[] = {
+struct snd_soc_dai sh4_ssi_dai[] = {
 {
 	.name			= "SSI0",
 	.id			= 0,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e148db9..83f1190 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -14,10 +14,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    12th Aug 2005   Initial version.
- *    25th Oct 2005   Working Codec, Interface and Platform registration.
- *
  *  TODO:
  *   o Add hw rules to enforce rates, etc.
  *   o More testing with other codecs/machines.
@@ -112,9 +108,9 @@
 }
 #endif
 
-static inline const char* get_dai_name(int type)
+static inline const char *get_dai_name(int type)
 {
-	switch(type) {
+	switch (type) {
 	case SND_SOC_DAI_AC97_BUS:
 	case SND_SOC_DAI_AC97:
 		return "AC97";
@@ -138,8 +134,8 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	int ret = 0;
 
 	mutex_lock(&pcm_mutex);
@@ -182,9 +178,11 @@
 	/* Check that the codec and cpu DAI's are compatible */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		runtime->hw.rate_min =
-			max(codec_dai->playback.rate_min, cpu_dai->playback.rate_min);
+			max(codec_dai->playback.rate_min,
+			    cpu_dai->playback.rate_min);
 		runtime->hw.rate_max =
-			min(codec_dai->playback.rate_max, cpu_dai->playback.rate_max);
+			min(codec_dai->playback.rate_max,
+			    cpu_dai->playback.rate_max);
 		runtime->hw.channels_min =
 			max(codec_dai->playback.channels_min,
 				cpu_dai->playback.channels_min);
@@ -197,9 +195,11 @@
 			codec_dai->playback.rates & cpu_dai->playback.rates;
 	} else {
 		runtime->hw.rate_min =
-			max(codec_dai->capture.rate_min, cpu_dai->capture.rate_min);
+			max(codec_dai->capture.rate_min,
+			    cpu_dai->capture.rate_min);
 		runtime->hw.rate_max =
-			min(codec_dai->capture.rate_max, cpu_dai->capture.rate_max);
+			min(codec_dai->capture.rate_max,
+			    cpu_dai->capture.rate_max);
 		runtime->hw.channels_min =
 			max(codec_dai->capture.channels_min,
 				cpu_dai->capture.channels_min);
@@ -229,7 +229,7 @@
 		goto machine_err;
 	}
 
-	dbg("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name);
+	dbg("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
 	dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
 	dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
 		runtime->hw.channels_max);
@@ -272,11 +272,11 @@
 	struct snd_soc_device *socdev =
 		container_of(work, struct snd_soc_device, delayed_work.work);
 	struct snd_soc_codec *codec = socdev->codec;
-	struct snd_soc_codec_dai *codec_dai;
+	struct snd_soc_dai *codec_dai;
 	int i;
 
 	mutex_lock(&pcm_mutex);
-	for(i = 0; i < codec->num_dai; i++) {
+	for (i = 0; i < codec->num_dai; i++) {
 		codec_dai = &codec->dai[i];
 
 		dbg("pop wq checking: %s status: %s waiting: %s\n",
@@ -287,12 +287,12 @@
 		/* are we waiting on this codec DAI stream */
 		if (codec_dai->pop_wait == 1) {
 
-			/* power down the codec to D1 if no longer active */
+			/* Reduce power if no longer active */
 			if (codec->active == 0) {
 				dbg("pop wq D1 %s %s\n", codec->name,
 					codec_dai->playback.stream_name);
-				snd_soc_dapm_device_event(socdev,
-					SNDRV_CTL_POWER_D1);
+				snd_soc_dapm_set_bias_level(socdev,
+					SND_SOC_BIAS_PREPARE);
 			}
 
 			codec_dai->pop_wait = 0;
@@ -300,12 +300,12 @@
 				codec_dai->playback.stream_name,
 				SND_SOC_DAPM_STREAM_STOP);
 
-			/* power down the codec power domain if no longer active */
+			/* Fall into standby if no longer active */
 			if (codec->active == 0) {
 				dbg("pop wq D3 %s %s\n", codec->name,
 					codec_dai->playback.stream_name);
-				snd_soc_dapm_device_event(socdev,
-					SNDRV_CTL_POWER_D3hot);
+				snd_soc_dapm_set_bias_level(socdev,
+					SND_SOC_BIAS_STANDBY);
 			}
 		}
 	}
@@ -323,8 +323,8 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	struct snd_soc_codec *codec = socdev->codec;
 
 	mutex_lock(&pcm_mutex);
@@ -365,8 +365,8 @@
 			SND_SOC_DAPM_STREAM_STOP);
 
 		if (codec->active == 0 && codec_dai->pop_wait == 0)
-			snd_soc_dapm_device_event(socdev,
-						SNDRV_CTL_POWER_D3hot);
+			snd_soc_dapm_set_bias_level(socdev,
+						SND_SOC_BIAS_STANDBY);
 	}
 
 	mutex_unlock(&pcm_mutex);
@@ -384,8 +384,8 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	struct snd_soc_codec *codec = socdev->codec;
 	int ret = 0;
 
@@ -434,14 +434,14 @@
 		else {
 			codec_dai->pop_wait = 0;
 			cancel_delayed_work(&socdev->delayed_work);
-			if (codec_dai->dai_ops.digital_mute)
-				codec_dai->dai_ops.digital_mute(codec_dai, 0);
+			snd_soc_dai_digital_mute(codec_dai, 0);
 		}
 	} else {
 		/* no delayed work - do we need to power up codec */
-		if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
+		if (codec->bias_level != SND_SOC_BIAS_ON) {
 
-			snd_soc_dapm_device_event(socdev,  SNDRV_CTL_POWER_D1);
+			snd_soc_dapm_set_bias_level(socdev,
+						    SND_SOC_BIAS_PREPARE);
 
 			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 				snd_soc_dapm_stream_event(codec,
@@ -452,9 +452,8 @@
 					codec_dai->capture.stream_name,
 					SND_SOC_DAPM_STREAM_START);
 
-			snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0);
-			if (codec_dai->dai_ops.digital_mute)
-				codec_dai->dai_ops.digital_mute(codec_dai, 0);
+			snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+			snd_soc_dai_digital_mute(codec_dai, 0);
 
 		} else {
 			/* codec already powered - power on widgets */
@@ -466,8 +465,8 @@
 				snd_soc_dapm_stream_event(codec,
 					codec_dai->capture.stream_name,
 					SND_SOC_DAPM_STREAM_START);
-			if (codec_dai->dai_ops.digital_mute)
-				codec_dai->dai_ops.digital_mute(codec_dai, 0);
+
+			snd_soc_dai_digital_mute(codec_dai, 0);
 		}
 	}
 
@@ -488,8 +487,8 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	int ret = 0;
 
 	mutex_lock(&pcm_mutex);
@@ -514,7 +513,7 @@
 	if (cpu_dai->ops.hw_params) {
 		ret = cpu_dai->ops.hw_params(substream, params);
 		if (ret < 0) {
-			printk(KERN_ERR "asoc: can't set interface %s hw params\n",
+			printk(KERN_ERR "asoc: interface %s hw params failed\n",
 				cpu_dai->name);
 			goto interface_err;
 		}
@@ -523,7 +522,7 @@
 	if (platform->pcm_ops->hw_params) {
 		ret = platform->pcm_ops->hw_params(substream, params);
 		if (ret < 0) {
-			printk(KERN_ERR "asoc: can't set platform %s hw params\n",
+			printk(KERN_ERR "asoc: platform %s hw params failed\n",
 				platform->name);
 			goto platform_err;
 		}
@@ -542,7 +541,7 @@
 		codec_dai->ops.hw_free(substream);
 
 codec_err:
-	if(machine->ops && machine->ops->hw_free)
+	if (machine->ops && machine->ops->hw_free)
 		machine->ops->hw_free(substream);
 
 	mutex_unlock(&pcm_mutex);
@@ -558,15 +557,15 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	struct snd_soc_codec *codec = socdev->codec;
 
 	mutex_lock(&pcm_mutex);
 
 	/* apply codec digital mute */
-	if (!codec->active && codec_dai->dai_ops.digital_mute)
-		codec_dai->dai_ops.digital_mute(codec_dai, 1);
+	if (!codec->active)
+		snd_soc_dai_digital_mute(codec_dai, 1);
 
 	/* free any machine hw params */
 	if (machine->ops && machine->ops->hw_free)
@@ -593,8 +592,8 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_dai_link *machine = rtd->dai;
 	struct snd_soc_platform *platform = socdev->platform;
-	struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai;
-	struct snd_soc_codec_dai *codec_dai = machine->codec_dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
 	int ret;
 
 	if (codec_dai->ops.trigger) {
@@ -631,16 +630,26 @@
 /* powers down audio subsystem for suspend */
 static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 {
- 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- 	struct snd_soc_machine *machine = socdev->machine;
- 	struct snd_soc_platform *platform = socdev->platform;
- 	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_machine *machine = socdev->machine;
+	struct snd_soc_platform *platform = socdev->platform;
+	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 	struct snd_soc_codec *codec = socdev->codec;
 	int i;
 
+	/* Due to the resume being scheduled into a workqueue we could
+	* suspend before that's finished - wait for it to complete.
+	 */
+	snd_power_lock(codec->card);
+	snd_power_wait(codec->card, SNDRV_CTL_POWER_D0);
+	snd_power_unlock(codec->card);
+
+	/* we're going to block userspace touching us until resume completes */
+	snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
+
 	/* mute any active DAC's */
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
 		if (dai->dai_ops.digital_mute && dai->playback.active)
 			dai->dai_ops.digital_mute(dai, 1);
 	}
@@ -652,8 +661,8 @@
 	if (machine->suspend_pre)
 		machine->suspend_pre(pdev, state);
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai  *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai  *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
 			cpu_dai->suspend(pdev, cpu_dai);
 		if (platform->suspend)
@@ -662,9 +671,9 @@
 
 	/* close any waiting streams and save state */
 	run_delayed_work(&socdev->delayed_work);
-	codec->suspend_dapm_state = codec->dapm_state;
+	codec->suspend_bias_level = codec->bias_level;
 
-	for(i = 0; i < codec->num_dai; i++) {
+	for (i = 0; i < codec->num_dai; i++) {
 		char *stream = codec->dai[i].playback.stream_name;
 		if (stream != NULL)
 			snd_soc_dapm_stream_event(codec, stream,
@@ -678,8 +687,8 @@
 	if (codec_dev->suspend)
 		codec_dev->suspend(pdev, state);
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
 			cpu_dai->suspend(pdev, cpu_dai);
 	}
@@ -690,21 +699,32 @@
 	return 0;
 }
 
-/* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+/* deferred resume work, so resume can complete before we finished
+ * setting our codec back up, which can be very slow on I2C
+ */
+static void soc_resume_deferred(struct work_struct *work)
 {
- 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- 	struct snd_soc_machine *machine = socdev->machine;
- 	struct snd_soc_platform *platform = socdev->platform;
- 	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+	struct snd_soc_device *socdev = container_of(work,
+						     struct snd_soc_device,
+						     deferred_resume_work);
+	struct snd_soc_machine *machine = socdev->machine;
+	struct snd_soc_platform *platform = socdev->platform;
+	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 	struct snd_soc_codec *codec = socdev->codec;
+	struct platform_device *pdev = to_platform_device(socdev->dev);
 	int i;
 
+	/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
+	 * so userspace apps are blocked from touching us
+	 */
+
+	dev_info(socdev->dev, "starting resume work\n");
+
 	if (machine->resume_pre)
 		machine->resume_pre(pdev);
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
 			cpu_dai->resume(pdev, cpu_dai);
 	}
@@ -712,8 +732,8 @@
 	if (codec_dev->resume)
 		codec_dev->resume(pdev);
 
-	for(i = 0; i < codec->num_dai; i++) {
-		char* stream = codec->dai[i].playback.stream_name;
+	for (i = 0; i < codec->num_dai; i++) {
+		char *stream = codec->dai[i].playback.stream_name;
 		if (stream != NULL)
 			snd_soc_dapm_stream_event(codec, stream,
 				SND_SOC_DAPM_STREAM_RESUME);
@@ -723,15 +743,15 @@
 				SND_SOC_DAPM_STREAM_RESUME);
 	}
 
-	/* unmute any active DAC's */
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
+	/* unmute any active DACs */
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
 		if (dai->dai_ops.digital_mute && dai->playback.active)
 			dai->dai_ops.digital_mute(dai, 0);
 	}
 
-	for(i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+	for (i = 0; i < machine->num_links; i++) {
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
 			cpu_dai->resume(pdev, cpu_dai);
 		if (platform->resume)
@@ -741,6 +761,22 @@
 	if (machine->resume_post)
 		machine->resume_post(pdev);
 
+	dev_info(socdev->dev, "resume work completed\n");
+
+	/* userspace can access us now we are back as we were before */
+	snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
+}
+
+/* powers up audio subsystem after a suspend */
+static int soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	dev_info(socdev->dev, "scheduling resume work\n");
+
+	if (!schedule_work(&socdev->deferred_resume_work))
+		dev_err(socdev->dev, "work item may be lost\n");
+
 	return 0;
 }
 
@@ -760,33 +796,38 @@
 
 	if (machine->probe) {
 		ret = machine->probe(pdev);
-		if(ret < 0)
+		if (ret < 0)
 			return ret;
 	}
 
 	for (i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->probe) {
-			ret = cpu_dai->probe(pdev);
-			if(ret < 0)
+			ret = cpu_dai->probe(pdev, cpu_dai);
+			if (ret < 0)
 				goto cpu_dai_err;
 		}
 	}
 
 	if (codec_dev->probe) {
 		ret = codec_dev->probe(pdev);
-		if(ret < 0)
+		if (ret < 0)
 			goto cpu_dai_err;
 	}
 
 	if (platform->probe) {
 		ret = platform->probe(pdev);
-		if(ret < 0)
+		if (ret < 0)
 			goto platform_err;
 	}
 
 	/* DAPM stream work */
 	INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+#ifdef CONFIG_PM
+	/* deferred resume work */
+	INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
+#endif
+
 	return 0;
 
 platform_err:
@@ -795,9 +836,9 @@
 
 cpu_dai_err:
 	for (i--; i >= 0; i--) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->remove)
-			cpu_dai->remove(pdev);
+			cpu_dai->remove(pdev, cpu_dai);
 	}
 
 	if (machine->remove)
@@ -824,9 +865,9 @@
 		codec_dev->remove(pdev);
 
 	for (i = 0; i < machine->num_links; i++) {
-		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+		struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->remove)
-			cpu_dai->remove(pdev);
+			cpu_dai->remove(pdev, cpu_dai);
 	}
 
 	if (machine->remove)
@@ -852,8 +893,8 @@
 	struct snd_soc_dai_link *dai_link, int num)
 {
 	struct snd_soc_codec *codec = socdev->codec;
-	struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai;
-	struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai;
+	struct snd_soc_dai *codec_dai = dai_link->codec_dai;
+	struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
 	struct snd_soc_pcm_runtime *rtd;
 	struct snd_pcm *pcm;
 	char new_name[64];
@@ -868,7 +909,7 @@
 	codec_dai->codec = socdev->codec;
 
 	/* check client and interface hw capabilities */
-	sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name,
+	sprintf(new_name, "%s %s-%s-%d", dai_link->stream_name, codec_dai->name,
 		get_dai_name(cpu_dai->type), num);
 
 	if (codec_dai->playback.channels_min)
@@ -879,7 +920,8 @@
 	ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,
 		capture, &pcm);
 	if (ret < 0) {
-		printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+		printk(KERN_ERR "asoc: can't create pcm for codec %s\n",
+			codec->name);
 		kfree(rtd);
 		return ret;
 	}
@@ -928,8 +970,9 @@
 		step = codec->reg_cache_step;
 
 	count += sprintf(buf, "%s registers\n", codec->name);
-	for(i = 0; i < codec->reg_cache_size; i += step)
-		count += sprintf(buf + count, "%2x: %4x\n", i, codec->read(codec, i));
+	for (i = 0; i < codec->reg_cache_size; i += step)
+		count += sprintf(buf + count, "%2x: %4x\n", i,
+			codec->read(codec, i));
 
 	return count;
 }
@@ -1072,7 +1115,7 @@
 	strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
 
 	/* create the pcms */
-	for(i = 0; i < machine->num_links; i++) {
+	for (i = 0; i < machine->num_links; i++) {
 		ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
 		if (ret < 0) {
 			printk(KERN_ERR "asoc: can't create pcm %s\n",
@@ -1102,7 +1145,7 @@
 	struct snd_soc_machine *machine = socdev->machine;
 	int ret = 0, i, ac97 = 0, err = 0;
 
-	for(i = 0; i < machine->num_links; i++) {
+	for (i = 0; i < machine->num_links; i++) {
 		if (socdev->machine->dai_link[i].init) {
 			err = socdev->machine->dai_link[i].init(codec);
 			if (err < 0) {
@@ -1111,7 +1154,7 @@
 				continue;
 			}
 		}
-		if (socdev->machine->dai_link[i].codec_dai->type == 
+		if (socdev->machine->dai_link[i].codec_dai->type ==
 			SND_SOC_DAI_AC97_BUS)
 			ac97 = 1;
 	}
@@ -1122,7 +1165,7 @@
 
 	ret = snd_card_register(codec->card);
 	if (ret < 0) {
-		printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n",
+		printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
 				codec->name);
 		goto out;
 	}
@@ -1146,7 +1189,7 @@
 
 	err = device_create_file(socdev->dev, &dev_attr_codec_reg);
 	if (err < 0)
-		printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
+		printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
 
 	mutex_unlock(&codec->mutex);
 
@@ -1166,13 +1209,13 @@
 {
 	struct snd_soc_codec *codec = socdev->codec;
 #ifdef CONFIG_SND_SOC_AC97_BUS
-	struct snd_soc_codec_dai *codec_dai;
+	struct snd_soc_dai *codec_dai;
 	int i;
 #endif
 
 	mutex_lock(&codec->mutex);
 #ifdef CONFIG_SND_SOC_AC97_BUS
-	for(i = 0; i < codec->num_dai; i++) {
+	for (i = 0; i < codec->num_dai; i++) {
 		codec_dai = &codec->dai[i];
 		if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) {
 			soc_ac97_dev_unregister(codec);
@@ -1282,7 +1325,8 @@
 	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
 		;
 	val = snd_soc_read(codec, e->reg);
-	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+	ucontrol->value.enumerated.item[0]
+		= (val >> e->shift_l) & (bitmask - 1);
 	if (e->shift_l != e->shift_r)
 		ucontrol->value.enumerated.item[1] =
 			(val >> e->shift_r) & (bitmask - 1);
@@ -1576,7 +1620,8 @@
 	val = val << shift;
 	val2 = val2 << shift;
 
-	if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0)
+	err = snd_soc_update_bits(codec, reg, val_mask, val);
+	if (err < 0)
 		return err;
 
 	err = snd_soc_update_bits(codec, reg2, val_mask, val2);
@@ -1584,6 +1629,204 @@
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
 
+/**
+ * snd_soc_info_volsw_s8 - signed mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	int max = (signed char)((kcontrol->private_value >> 16) & 0xff);
+	int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = max-min;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
+
+/**
+ * snd_soc_get_volsw_s8 - signed mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+	int val = snd_soc_read(codec, reg);
+
+	ucontrol->value.integer.value[0] =
+		((signed char)(val & 0xff))-min;
+	ucontrol->value.integer.value[1] =
+		((signed char)((val >> 8) & 0xff))-min;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
+
+/**
+ * snd_soc_put_volsw_sgn - signed mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int min = (signed char)((kcontrol->private_value >> 24) & 0xff);
+	unsigned short val;
+
+	val = (ucontrol->value.integer.value[0]+min) & 0xff;
+	val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
+
+	return snd_soc_update_bits(codec, reg, 0xffff, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
+
+/**
+ * snd_soc_dai_set_sysclk - configure DAI system or master clock.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
+ *
+ * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
+ */
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+	unsigned int freq, int dir)
+{
+	if (dai->dai_ops.set_sysclk)
+		return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
+
+/**
+ * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
+ * @dai: DAI
+ * @clk_id: DAI specific clock divider ID
+ * @div: new clock divisor.
+ *
+ * Configures the clock dividers. This is used to derive the best DAI bit and
+ * frame clocks from the system or master clock. It's best to set the DAI bit
+ * and frame clocks as low as possible to save system power.
+ */
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+	int div_id, int div)
+{
+	if (dai->dai_ops.set_clkdiv)
+		return dai->dai_ops.set_clkdiv(dai, div_id, div);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
+
+/**
+ * snd_soc_dai_set_pll - configure DAI PLL.
+ * @dai: DAI
+ * @pll_id: DAI specific PLL ID
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
+ *
+ * Configures and enables PLL to generate output clock based on input clock.
+ */
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+	int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	if (dai->dai_ops.set_pll)
+		return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
+
+/**
+ * snd_soc_dai_set_fmt - configure DAI hardware audio format.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @fmt: SND_SOC_DAIFMT_ format value.
+ *
+ * Configures the DAI hardware format and clocking.
+ */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	if (dai->dai_ops.set_fmt)
+		return dai->dai_ops.set_fmt(dai, fmt);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
+
+/**
+ * snd_soc_dai_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @mask: DAI specific mask representing used slots.
+ * @slots: Number of slots in use.
+ *
+ * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
+ * specific.
+ */
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int mask, int slots)
+{
+	if (dai->dai_ops.set_sysclk)
+		return dai->dai_ops.set_tdm_slot(dai, mask, slots);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
+
+/**
+ * snd_soc_dai_set_tristate - configure DAI system or master clock.
+ * @dai: DAI
+ * @tristate: tristate enable
+ *
+ * Tristates the DAI so that others can use it.
+ */
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	if (dai->dai_ops.set_sysclk)
+		return dai->dai_ops.set_tristate(dai, tristate);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
+
+/**
+ * snd_soc_dai_digital_mute - configure DAI system or master clock.
+ * @dai: DAI
+ * @mute: mute enable
+ *
+ * Mutes the DAI DAC.
+ */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	if (dai->dai_ops.digital_mute)
+		return dai->dai_ops.digital_mute(dai, mute);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
+
 static int __devinit snd_soc_init(void)
 {
 	printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
@@ -1592,7 +1835,7 @@
 
 static void snd_soc_exit(void)
 {
- 	platform_driver_unregister(&soc_driver);
+	platform_driver_unregister(&soc_driver);
 }
 
 module_init(snd_soc_init);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index af3326c..2c87061 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -10,11 +10,6 @@
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *
- *  Revision history
- *    12th Aug 2005   Initial version.
- *    25th Oct 2005   Implemented path power domain.
- *    18th Dec 2005   Implemented machine and stream level power domain.
- *
  *  Features:
  *    o Changes power status of internal codec blocks depending on the
  *      dynamic configuration of codec internal audio paths and active
@@ -50,23 +45,10 @@
 #include <sound/initval.h>
 
 /* debug */
-#define DAPM_DEBUG 0
-#if DAPM_DEBUG
+#ifdef DEBUG
 #define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
-#define dbg(format, arg...) printk(format, ## arg)
 #else
 #define dump_dapm(codec, action)
-#define dbg(format, arg...)
-#endif
-
-#define POP_DEBUG 0
-#if POP_DEBUG
-#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
-#define pop_wait(time) schedule_timeout_uninterruptible(msecs_to_jiffies(time))
-#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
-#else
-#define pop_dbg(format, arg...)
-#define pop_wait(time)
 #endif
 
 /* dapm power sequences - make this per codec in the future */
@@ -85,6 +67,28 @@
 module_param(dapm_status, int, 0);
 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
 
+static unsigned int pop_time;
+
+static void pop_wait(void)
+{
+	if (pop_time)
+		schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
+}
+
+static void pop_dbg(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+
+	if (pop_time) {
+		vprintk(fmt, args);
+		pop_wait();
+	}
+
+	va_end(args);
+}
+
 /* create a new dapm widget */
 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
 	const struct snd_soc_dapm_widget *_widget)
@@ -222,11 +226,12 @@
 	change = old != new;
 	if (change) {
 		pop_dbg("pop test %s : %s in %d ms\n", widget->name,
-			widget->power ? "on" : "off", POP_TIME);
+			widget->power ? "on" : "off", pop_time);
 		snd_soc_write(codec, widget->reg, new);
-		pop_wait(POP_TIME);
+		pop_wait();
 	}
-	dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change);
+	pr_debug("reg %x old %x new %x change %d\n", widget->reg,
+		 old, new, change);
 	return change;
 }
 
@@ -448,6 +453,25 @@
 }
 
 /*
+ * Handler for generic register modifier widget.
+ */
+int dapm_reg_event(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol, int event)
+{
+	unsigned int val;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		val = w->on_val;
+	else
+		val = w->off_val;
+
+	snd_soc_update_bits(w->codec, -(w->reg + 1),
+			    w->mask << w->shift, val << w->shift);
+
+	return 0;
+}
+
+/*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
  *
@@ -565,8 +589,8 @@
 			/* call any power change event handlers */
 			if (power_change) {
 				if (w->event) {
-					dbg("power %s event for %s flags %x\n",
-						w->power ? "on" : "off", w->name, w->event_flags);
+					pr_debug("power %s event for %s flags %x\n",
+						 w->power ? "on" : "off", w->name, w->event_flags);
 					if (power) {
 						/* power up event */
 						if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
@@ -608,7 +632,7 @@
 	return ret;
 }
 
-#if DAPM_DEBUG
+#ifdef DEBUG
 static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
 {
 	struct snd_soc_dapm_widget *w;
@@ -693,8 +717,10 @@
 			path->connect = 0; /* old connection must be powered down */
 	}
 
-	if (found)
+	if (found) {
 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+		dump_dapm(widget->codec, "mux power update");
+	}
 
 	return 0;
 }
@@ -730,8 +756,10 @@
 		break;
 	}
 
-	if (found)
+	if (found) {
 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+		dump_dapm(widget->codec, "mixer power update");
+	}
 
 	return 0;
 }
@@ -768,21 +796,18 @@
 		}
 	}
 
-	switch(codec->dapm_state){
-	case SNDRV_CTL_POWER_D0:
-		state = "D0";
+	switch (codec->bias_level) {
+	case SND_SOC_BIAS_ON:
+		state = "On";
 		break;
-	case SNDRV_CTL_POWER_D1:
-		state = "D1";
+	case SND_SOC_BIAS_PREPARE:
+		state = "Prepare";
 		break;
-	case SNDRV_CTL_POWER_D2:
-		state = "D2";
+	case SND_SOC_BIAS_STANDBY:
+		state = "Standby";
 		break;
-	case SNDRV_CTL_POWER_D3hot:
-		state = "D3hot";
-		break;
-	case SNDRV_CTL_POWER_D3cold:
-		state = "D3cold";
+	case SND_SOC_BIAS_OFF:
+		state = "Off";
 		break;
 	}
 	count += sprintf(buf + count, "PM State: %s\n", state);
@@ -792,20 +817,51 @@
 
 static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
 
+/* pop/click delay times */
+static ssize_t dapm_pop_time_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", pop_time);
+}
+
+static ssize_t dapm_pop_time_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+
+{
+	unsigned long val;
+
+	if (strict_strtoul(buf, 10, &val) >= 0)
+		pop_time = val;
+	else
+		printk(KERN_ERR "Unable to parse pop_time setting\n");
+
+	return count;
+}
+
+static DEVICE_ATTR(dapm_pop_time, 0744, dapm_pop_time_show,
+		   dapm_pop_time_store);
+
 int snd_soc_dapm_sys_add(struct device *dev)
 {
 	int ret = 0;
 
-	if (dapm_status)
+	if (dapm_status) {
 		ret = device_create_file(dev, &dev_attr_dapm_widget);
 
+		if (ret == 0)
+			ret = device_create_file(dev, &dev_attr_dapm_pop_time);
+	}
+
 	return ret;
 }
 
 static void snd_soc_dapm_sys_remove(struct device *dev)
 {
-	if (dapm_status)
+	if (dapm_status) {
+		device_remove_file(dev, &dev_attr_dapm_pop_time);
 		device_remove_file(dev, &dev_attr_dapm_widget);
+	}
 }
 
 /* free all dapm widgets and resources */
@@ -826,8 +882,25 @@
 	}
 }
 
+static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
+	char *pin, int status)
+{
+	struct snd_soc_dapm_widget *w;
+
+	list_for_each_entry(w, &codec->dapm_widgets, list) {
+		if (!strcmp(w->name, pin)) {
+			pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+			w->connected = status;
+			return 0;
+		}
+	}
+
+	pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+	return -EINVAL;
+}
+
 /**
- * snd_soc_dapm_sync_endpoints - scan and power dapm paths
+ * snd_soc_dapm_sync - scan and power dapm paths
  * @codec: audio codec
  *
  * Walks all dapm audio paths and powers widgets according to their
@@ -835,27 +908,16 @@
  *
  * Returns 0 for success.
  */
-int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
+int snd_soc_dapm_sync(struct snd_soc_codec *codec)
 {
-	return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+	int ret = dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+	dump_dapm(codec, "sync");
+	return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
-/**
- * snd_soc_dapm_connect_input - connect dapm widgets
- * @codec: audio codec
- * @sink: name of target widget
- * @control: mixer control name
- * @source: name of source name
- *
- * Connects 2 dapm widgets together via a named audio path. The sink is
- * the widget receiving the audio signal, whilst the source is the sender
- * of the audio signal.
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
-	const char * control, const char *source)
+static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
+	const char *sink, const char *control, const char *source)
 {
 	struct snd_soc_dapm_path *path;
 	struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
@@ -957,9 +1019,64 @@
 	kfree(path);
 	return ret;
 }
+
+/**
+ * snd_soc_dapm_connect_input - connect dapm widgets
+ * @codec: audio codec
+ * @sink: name of target widget
+ * @control: mixer control name
+ * @source: name of source name
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * This function has been deprecated in favour of snd_soc_dapm_add_routes().
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
+	const char *control, const char *source)
+{
+	return snd_soc_dapm_add_route(codec, sink, control, source);
+}
 EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
 
 /**
+ * snd_soc_dapm_add_routes - Add routes between DAPM widgets
+ * @codec: codec
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Connects 2 dapm widgets together via a named audio path. The sink is
+ * the widget receiving the audio signal, whilst the source is the sender
+ * of the audio signal.
+ *
+ * Returns 0 for success else error. On error all resources can be freed
+ * with a call to snd_soc_card_free().
+ */
+int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+			    const struct snd_soc_dapm_route *route, int num)
+{
+	int i, ret;
+
+	for (i = 0; i < num; i++) {
+		ret = snd_soc_dapm_add_route(codec, route->sink,
+					     route->control, route->source);
+		if (ret < 0) {
+			printk(KERN_ERR "Failed to add route %s->%s\n",
+			       route->source,
+			       route->sink);
+			return ret;
+		}
+		route++;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
+
+/**
  * snd_soc_dapm_new_widgets - add new dapm widgets
  * @codec: audio codec
  *
@@ -1234,6 +1351,33 @@
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
 
 /**
+ * snd_soc_dapm_new_controls - create new dapm controls
+ * @codec: audio codec
+ * @widget: widget array
+ * @num: number of widgets
+ *
+ * Creates new DAPM controls based upon the templates.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+	const struct snd_soc_dapm_widget *widget,
+	int num)
+{
+	int i, ret;
+
+	for (i = 0; i < num; i++) {
+		ret = snd_soc_dapm_new_control(codec, widget);
+		if (ret < 0)
+			return ret;
+		widget++;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
+
+
+/**
  * snd_soc_dapm_stream_event - send a stream event to the dapm core
  * @codec: audio codec
  * @stream: stream name
@@ -1257,8 +1401,8 @@
 	{
 		if (!w->sname)
 			continue;
-		dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
-			stream, event);
+		pr_debug("widget %s\n %s stream %s event %d\n",
+			 w->name, w->sname, stream, event);
 		if (strstr(w->sname, stream)) {
 			switch(event) {
 			case SND_SOC_DAPM_STREAM_START:
@@ -1294,53 +1438,81 @@
 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
 /**
- * snd_soc_dapm_device_event - send a device event to the dapm core
+ * snd_soc_dapm_set_bias_level - set the bias level for the system
  * @socdev: audio device
- * @event: device event
+ * @level: level to configure
  *
- * Sends a device event to the dapm core. The core then makes any
- * necessary machine or codec power changes..
+ * Configure the bias (power) levels for the SoC audio device.
  *
  * Returns 0 for success else error.
  */
-int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event)
+int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+				enum snd_soc_bias_level level)
 {
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_machine *machine = socdev->machine;
+	int ret = 0;
 
-	if (machine->dapm_event)
-		machine->dapm_event(machine, event);
-	if (codec->dapm_event)
-		codec->dapm_event(codec, event);
-	return 0;
+	if (machine->set_bias_level)
+		ret = machine->set_bias_level(machine, level);
+	if (ret == 0 && codec->set_bias_level)
+		ret = codec->set_bias_level(codec, level);
+
+	return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event);
 
 /**
- * snd_soc_dapm_set_endpoint - set audio endpoint status
- * @codec: audio codec
- * @endpoint: audio signal endpoint (or start point)
- * @status: point status
+ * snd_soc_dapm_enable_pin - enable pin.
+ * @snd_soc_codec: SoC codec
+ * @pin: pin name
  *
- * Set audio endpoint status - connected or disconnected.
- *
- * Returns 0 for success else error.
+ * Enables input/output pin and it's parents or children widgets iff there is
+ * a valid audio route and active audio stream.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
  */
-int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
-	char *endpoint, int status)
+int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin)
+{
+	return snd_soc_dapm_set_pin(codec, pin, 1);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
+
+/**
+ * snd_soc_dapm_disable_pin - disable pin.
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Disables input/output pin and it's parents or children widgets.
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin)
+{
+	return snd_soc_dapm_set_pin(codec, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
+
+/**
+ * snd_soc_dapm_get_pin_status - get audio pin status
+ * @codec: audio codec
+ * @pin: audio signal pin endpoint (or start point)
+ *
+ * Get audio pin status - connected or disconnected.
+ *
+ * Returns 1 for connected otherwise 0.
+ */
+int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin)
 {
 	struct snd_soc_dapm_widget *w;
 
 	list_for_each_entry(w, &codec->dapm_widgets, list) {
-		if (!strcmp(w->name, endpoint)) {
-			w->connected = status;
-			return 0;
-		}
+		if (!strcmp(w->name, pin))
+			return w->connected;
 	}
 
-	return -ENODEV;
+	return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
 
 /**
  * snd_soc_dapm_free - free dapm resources
diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig
index 079e22a..d75deba 100644
--- a/sound/sparc/Kconfig
+++ b/sound/sparc/Kconfig
@@ -1,11 +1,17 @@
 # ALSA Sparc drivers
 
-menu "ALSA Sparc devices"
-	depends on SND!=n && SPARC
+menuconfig SND_SPARC
+	bool "Sparc sound devices"
+	depends on SPARC
+	default y
+	help
+	  Support for sound devices specific to Sun SPARC architectures.
+
+if SND_SPARC
 
 config SND_SUN_AMD7930
 	tristate "Sun AMD7930"
-	depends on SBUS && SND
+	depends on SBUS
 	select SND_PCM
 	help
 	  Say Y here to include support for AMD7930 sound device on Sun.
@@ -15,7 +21,6 @@
 
 config SND_SUN_CS4231
 	tristate "Sun CS4231"
-	depends on SND
 	select SND_PCM
 	help
 	  Say Y here to include support for CS4231 sound device on Sun.
@@ -25,7 +30,7 @@
 
 config SND_SUN_DBRI
 	tristate "Sun DBRI"
-	depends on SND && SBUS
+	depends on SBUS
 	select SND_PCM
 	help
 	  Say Y here to include support for DBRI sound device on Sun.
@@ -33,4 +38,4 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-sun-dbri.
 
-endmenu
+endif	# SND_SPARC
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index 3d00e07..ee2e1b4 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -2490,7 +2490,7 @@
 }
 #endif
 
-void __devinit snd_dbri_proc(struct snd_card *card)
+static void __devinit snd_dbri_proc(struct snd_card *card)
 {
 	struct snd_dbri *dbri = card->private_data;
 	struct snd_info_entry *entry;
diff --git a/sound/spi/Kconfig b/sound/spi/Kconfig
index 0d08c29..e6485be 100644
--- a/sound/spi/Kconfig
+++ b/sound/spi/Kconfig
@@ -1,7 +1,13 @@
 #SPI drivers
 
-menu "SPI devices"
-	depends on SND != n
+menuconfig SND_SPI
+	bool "SPI sound devices"
+	depends on SPI
+	default y
+	help
+	  Support for sound devices connected via the SPI bus.
+
+if SND_SPI
 
 config SND_AT73C213
 	tristate "Atmel AT73C213 DAC driver"
@@ -28,4 +34,5 @@
 
 	  Set to 48000 Hz by default.
 
-endmenu
+endif	# SND_SPI
+
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 9351b8a..ffcdc8f 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -1,11 +1,16 @@
 # ALSA USB drivers
 
-menu "USB devices"
-	depends on SND!=n && USB!=n
+menuconfig SND_USB
+	bool "USB sound devices"
+	depends on USB
+	default y
+	help
+	  Support for sound devices connected via the USB bus.
+
+if SND_USB && USB
 
 config SND_USB_AUDIO
 	tristate "USB Audio/MIDI driver"
-	depends on SND && USB
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -18,7 +23,7 @@
 
 config SND_USB_USX2Y
 	tristate "Tascam US-122, US-224 and US-428 USB driver"
-	depends on SND && USB && (X86 || PPC || ALPHA)
+	depends on X86 || PPC || ALPHA
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -31,7 +36,6 @@
 
 config SND_USB_CAIAQ
 	tristate "Native Instruments USB audio devices"
-	depends on SND && USB
 	select SND_HWDEP
 	select SND_RAWMIDI
 	select SND_PCM
@@ -63,5 +67,5 @@
 	   * Native Instruments Kore Controller 2
 	   * Native Instruments Audio Kontrol 1
 
-endmenu
+endif	# SND_USB
 
diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c
index 24970a5..b3a6033 100644
--- a/sound/usb/caiaq/caiaq-audio.c
+++ b/sound/usb/caiaq/caiaq-audio.c
@@ -637,6 +637,7 @@
 	switch (dev->chip.usb_id) {
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
+	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO):
 		dev->samplerates |= SNDRV_PCM_RATE_88200;
 		dev->samplerates |= SNDRV_PCM_RATE_192000;
 		break;
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index a972f77..8317508 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -42,14 +42,15 @@
 #endif
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.6");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.8");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
 			 "{Native Instruments, RigKontrol3},"
 			 "{Native Instruments, Kore Controller},"
 			 "{Native Instruments, Kore Controller 2},"
-			 "{Native Instruments, Audio Kontrol 1}"
-			 "{Native Instruments, Audio 8 DJ}}");
+			 "{Native Instruments, Audio Kontrol 1},"
+			 "{Native Instruments, Audio 8 DJ},"
+			 "{Native Instruments, Session I/O}}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
 static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -110,6 +111,11 @@
 		.idVendor =     USB_VID_NATIVEINSTRUMENTS,
 		.idProduct =    USB_PID_AUDIO8DJ
 	},
+	{
+		.match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+		.idVendor =     USB_VID_NATIVEINSTRUMENTS,
+		.idProduct =    USB_PID_SESSIONIO
+	},
 	{ /* terminator */ }
 };
 
diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h
index 96a4913..f9fbdba 100644
--- a/sound/usb/caiaq/caiaq-device.h
+++ b/sound/usb/caiaq/caiaq-device.h
@@ -11,6 +11,7 @@
 #define USB_PID_KORECONTROLLER2	0x4712
 #define USB_PID_AK1		0x0815
 #define USB_PID_AUDIO8DJ	0x1978
+#define USB_PID_SESSIONIO	0x1915
 
 #define EP1_BUFSIZE 64
 #define CAIAQ_USB_STR_LEN 0xff
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index 410be4a..b8cfb7c 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -819,10 +819,6 @@
 		return "device disabled";
 	case -EHOSTUNREACH:
 		return "device suspended";
-#ifndef CONFIG_USB_EHCI_SPLIT_ISO
-	case -ENOSYS:
-		return "enable CONFIG_USB_EHCI_SPLIT_ISO to play through a hub";
-#endif
 	case -EINVAL:
 	case -EAGAIN:
 	case -EFBIG:
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 82a8d14..9ea726c 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -210,6 +210,11 @@
 YAMAHA_DEVICE(0x1043, NULL),
 YAMAHA_DEVICE(0x1044, NULL),
 YAMAHA_DEVICE(0x1045, NULL),
+YAMAHA_INTERFACE(0x104e, 0, NULL),
+YAMAHA_DEVICE(0x104f, NULL),
+YAMAHA_DEVICE(0x1050, NULL),
+YAMAHA_DEVICE(0x1051, NULL),
+YAMAHA_DEVICE(0x1052, NULL),
 YAMAHA_DEVICE(0x2000, "DGP-7"),
 YAMAHA_DEVICE(0x2001, "DGP-5"),
 YAMAHA_DEVICE(0x2002, NULL),
@@ -1379,6 +1384,39 @@
 	}
 },
 
+{
+	/* Roland SonicCell */
+	USB_DEVICE(0x0582, 0x00c2),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.vendor_name = "Roland",
+		.product_name = "SonicCell",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const struct snd_usb_midi_endpoint_info) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
+
+
 /* Guillemot devices */
 {
 	/*